C# 用犀牛嘲弄干

C# 用犀牛嘲弄干,c#,unit-testing,nunit,rhino-mocks,dry,C#,Unit Testing,Nunit,Rhino Mocks,Dry,我正在寻找使以下内容更简洁的方法 public class MyTests { IPresenter presenter; [SetUp] public void SetUp() { presenter = MockRepository.GenerateStub<IPresenter>(); } ... } 公共类MyTests { i演示者; [设置] 公共作废设置() { presenter=MockRepos

我正在寻找使以下内容更简洁的方法

public class MyTests
{
    IPresenter presenter;

    [SetUp]
    public void SetUp()
    {
        presenter = MockRepository.GenerateStub<IPresenter>();
    }

    ...
}
公共类MyTests
{
i演示者;
[设置]
公共作废设置()
{
presenter=MockRepository.GenerateSub();
}
...
}
特别是在创建模拟时再次指定类型似乎是多余的。例如,我可以这样编写,并使用反射获取类型并自动创建存根:

public class MyTests
{
    IPresenter presenter;

    [SetUp]
    public void SetUp()
    {
        Stub(x => x.presenter);
    }

    void Stub(Expression<Func<MyTests, object>> expression)
    {
        ...
    }
}
公共类MyTests
{
i演示者;
[设置]
公共作废设置()
{
存根(x=>x.presenter);
}
空存根(表达式)
{
...
}
}
这将起作用,但编译器无法再检测到已分配了presenter并开始发出警告。这也让ReSharper非常不高兴


有人能提出更好的方法吗?

是的,根本不要使用
[Setup]
和成员变量,而是使用创建方法编写

Fixture对象将只保存适当的mock和Fixture的其他部分

我个人使用它,并将其设置为要引导的自动模拟容器,因此我不必编写任何模拟代码,除非我需要显式定义某些行为

下面是最近的一个单元测试示例:

[TestMethod]
public void DeleteProductWillDeleteProductFromRepository()
{
    // Fixture setup
    var fixture = new ServiceFixture();
    var id = fixture.CreateAnonymous<int>();
    var repMock = fixture.FreezeMoq<ProductRepository>();

    var sut = fixture.CreateAnonymous<ProductManagementService>();
    // Exercise system
    sut.DeleteProduct(id);
    // Verify outcome
    repMock.Verify(r => r.DeleteProduct(id));
    // Teardown
}
[TestMethod]
public void DeleteProductWillDeleteProductFromRepository()
{
//夹具设置
var fixture=新的ServiceFixture();
var id=fixture.CreateAnonymous();
var repMock=fixture.FreezeMoq();
var sut=fixture.CreateAnonymous();
//运动系统
sut.DeleteProduct(id);
//核实结果
repMock.Verify(r=>r.DeleteProduct(id));
//拆卸
}

在这种情况下,
repMock
是由Moq创建的,但我可以将其设置为使用Rhino mock。

这可能有争议,但在单元测试中,我倾向于可读性,而不是干性*

换句话说,设置方法在我的单元测试中并不存在。它们仅用于集成测试。我相信我们也会采取这一立场

因此,为了回答您的问题,我真的不担心在每个需要模拟演示的测试中设置模拟演示者。有些测试可能不需要模拟演示者,因此在测试运行之前设置一个演示者是不必要的

**当然,我的单元测试平均跨越10行,如果这增加了测试设置的范围(遵循AAA-Arrange、Act-Assert),那么只有这样我才能删除重复并创建帮助器方法。为了澄清这一点,对于更干净的测试,您可以创建一个包含helper方法和其他设置代码的基本测试类。*

这是C#的一个常见问题,它不能从方法结果推断类型(与Java不同),而且在许多情况下都很麻烦(仅举另一个例子,您希望实现反序列化方法)。就个人而言,我不喜欢使用var关键字,因为我希望看到对象的确切类型,我更希望跳过模板中的类型名称


不管怎么说,如果我的测试很好而且很小,我倾向于初始化每个测试用例中的所有模拟。这样,你可以将每个测试与其他测试分开来看,并立即看到那里发生了什么。如果它们变得很长,尽管我只是使用你在问题开始时粘贴的令人讨厌的方式。

Michael Feathers有一个答案创建构建器并在测试中使用它,而不是在各种设置中使用它

比如:

    //The Class to Test
    public class ObjectY
    {
        public string DoThis(IObjectX objectX)
        {
            return objectX.id + objectX.name;
        }
    }


    [Test]
    //The test
    public void CreaeteTestData()
    {
        //Almost prosa style creation of test data 
        var testData = new ObjectXBuilder().WithId(123).WithName("ABC").Build();

        Assert.That(new ObjectY().DoThis(testData), Is.EqualTo("123ABC"));

    }


    //The Builder class - Provides easy creation testdata.
    internal class ObjectXBuilder
    {
        private MockRepository _mockRepository;
        private IObjectX _objectX;

        public ObjectXBuilder()
        {
            _mockRepository = new MockRepository();
            _objectX = _mockRepository.Stub<IObjectX>();
        }

        public ObjectXBuilder WithName(string name)
        {
            _objectX.name = name;
            return this;
        }

        public ObjectXBuilder WithId(long id)
        {
            _objectX.id = id;
            return this; 
        }

        public IObjectX Build()
        {
            _mockRepository.ReplayAll();
            return _objectX;
        }

    }
//要测试的类
公共类反对
{
公共字符串DoThis(IObjectX objectX)
{
返回objectX.id+objectX.name;
}
}
[测试]
//测试
public void CreaeteTestData()
{
//几乎是一种测试数据的创建方式
var testData=new ObjectXBuilder().WithId(123).WithName(“ABC”).Build();
Assert.That(new ObjectY().DoThis(testData),是.EqualTo(“123ABC”);
}
//Builder类-提供简单的testdata创建。
内部类ObjectXBuilder
{
私有MockRepository\u MockRepository;
私有IObjectX_objectX;
公共对象XBuilder()
{
_mockRepository=新建mockRepository();
_objectX=_mockRepository.Stub();
}
具有名称(字符串名称)的公共对象XBuilder
{
_objectX.name=名称;
归还这个;
}
id为(长id)的公共对象XBuilder
{
_objectX.id=id;
归还这个;
}
公共IObjectX构建()
{
_mockRepository.ReplayAll();
返回对象x;
}
}

我基本上同意这一点(因此是+1),但我还想建议,如果要有很多测试用例使用模拟,可以为测试创建一个内部类。@finglas这是一个一致的循环。让我们互相拍拍对方的背;)干代码对可读性有很大的帮助。我正在寻找减少噪音的方法,以便能够看到重要的部分。@一般错误:如果其他开发人员打算使用此代码库,那么您的第一个示例比使用lambda表达式的第二个版本更容易找到新人。引用乔恩在《简单几乎总是比聪明好》一书中的话,我不同意。对于代码,是的,对于测试代码,虽然不是那么多。例如,必须向上滚动屏幕以查看如何设置测试是一件痛苦的事情。当测试失败时尤其如此,需要有人跟进并修复它们。我(和其他人)更喜欢阅读简单的独立测试,而不是使用更复杂的语言功能来减少重复!您正在用一个成员变量+初始值设定项来交换每个方法中的额外设置,这些设置将与多个测试相加。自动模拟是我应该看的东西