1

选项1。ParentTest.CreateChild()它本质上Child也是在测试构造函数(我不喜欢)。

public class Parent
{
    public Child Child { get; private set; }

    public void CreateChild(int param1, string param2)
    {
        Child = new Child(param1, param2);
    }
}

public class Child
{
    public int Param1 { get; private set; }
    public string Param2 { get; private set; }

    public Child(int param1, string param2)
    {
        Param1 = param1;
        Param2 = param2;
    }
}

[TestFixture]
public class ParentTest
{
    [Test]
    public void CreateChild()
    {
        const int param1 = 23;
        const string param2 = "param2";
        var parent = new Parent();

        parent.CreateChild(param1, param2);

        Assert.That(parent.Child, Is.Not.Null);
        Assert.That(parent.Child.Param1, Is.EqualTo(param1));
        Assert.That(parent.Child.Param2, Is.EqualTo(param2));
    }
}

[TestFixture]
public class ChildTest
{
    [Test]
    public void Create()
    {
        const int param1 = 23;
        const string param2 = "param2";

        var child = new Child(param1, param2);

        Assert.That(child.Param1, Is.EqualTo(param1));
        Assert.That(child.Param2, Is.EqualTo(param2));            
    }
}

选项 2. 使用工厂服务创建Child实例。在这里,我不太确定将服务作为域方法参数传递的想法。

public class Parent
{
    public Child Child { get; private set; }

    public void CreateChild(int param1, string param2, IChildFactory childFactory)
    {
        Child = childFactory.Create(param1, param2);
    }
}

public class Child
{
    public int Param1 { get; private set; }
    public string Param2 { get; private set; }

    protected Child() {} // to be able to generate stub

    public Child(int param1, string param2)
    {
        Param1 = param1;
        Param2 = param2;
    }
}

public interface IChildFactory
{
    Child Create(int param1, string param2);
}

public class ChildFactory : IChildFactory
{
    public Child Create(int param1, string param2)
    {
        return new Child(param1, param2);
    }
}     

[TestFixture]
public class ParentTest
{
    [Test]
    public void CreateChild()
    {
        const int param1 = 23;
        const string param2 = "param2";
        var child = MockRepository.GenerateStub<Child>();
        var childFactory = MockRepository.GenerateStub<IChildFactory>();
        childFactory.Stub(x => x.Create(param1, param2)).Return(child);
        var parent = new Parent();

        parent.CreateChild(param1, param2, childFactory);

        Assert.That(parent.Child, Is.SameAs(child));
    }    
}

[TestFixture]
public class ChildTest
{
    // empty as contructor is tested in ChildFactoryTest
}

[TestFixture]
public class ChildFactoryTest
{
    [Test]
    public void Create()
    {
        const int param1 = 23;
        const string param2 = "param2";
        var childFactory = new ChildFactory();

        var child = childFactory.Create(param1, param2);

        Assert.That(child.Param1, Is.EqualTo(param1));
        Assert.That(child.Param2, Is.EqualTo(param2));
    }
}

我实际上更喜欢选项 2,因为每次域方法创建另一个域实体时,我不必测试创建的实体的属性(在工厂测试中只测试一次)。

有人有更好的解决方案吗?

更新: 来自@user1494736 的回答:“我认为,如果您正在测试 Parent,您还应该测试 Child 构造函数”。您可能有另一种Parent创建方法Child,并且在其测试中您需要Child再次测试属性。一般来说,我不想在Child每次Parent方法调用它时都测试构造函数的结果。让我们假设Child构造函数根据构造函数参数进行一些复杂的计算。您将Child仅对构造函数进行几次测试,以测试各种组合和结果。现在,如果您要在测试中测试Child构造函数Parent,并且您将修改Child构造函数实现,这些Parent测试会开始失败(我不喜欢的事情,也是我更喜欢选项 2 的主要原因,即使我也有一些保留意见)

4

2 回答 2

2

您可以修改选项 2 以将ChildFactoryin 传递给Parent的构造函数,而不是将其作为参数传递给CreateChild()

public class Parent
{
    private IChildFactory _childFactory;

    public Parent(IChildFactory childFactory)
    {
        _childFactory = childFactory;
    }

    public Child Child { get; private set; }

    public void CreateChild(int param1, string param2)
    {
        Child = _childFactory.Create(param1, param2);
    }
}
于 2012-07-13T18:15:23.480 回答
0

您可以提取创建并使方法虚拟:

public class Parent
{
    public Child Child { get; private set; }

    public void CreateChild(int param1, string param2)
    {
        Child = new ObtainChild(param1, param2);
    }
    virtual public Child ObtainChild(int param1, string param2)
    {
        return new Child(param1, param2);
    }
}

然后创建一个具有不同实现的“可测试”子类,并对其进行测试: public class TestableParent : Parent { override public Child GainChild(int param1, string param2) { //在这里做任何你想做的事情 } }

或者:

公共类 TestableParent : Parent { Func GetChildImplementation; 覆盖公共子获取子(int param1,字符串参数2){返回获取子实现();} }

请注意,让 Child 实现 IChild 接口并使用它会使事情变得更容易......

或者,您可以在不使用“可测试”类的情况下实现相同的效果,只需使用可以处理此问题的模拟框架(例如 Moq): var testableParent = new Mock(/* you can put parents parameters here*/); testableParent.Setup(parent => parent.ObtainChild(/* 匹配参数*/).Returns(/* return something*/);

BTW:我个人认为白盒测试是一个非常糟糕的主意......而且由于大多数单元测试都是白盒测试,我通常认为大多数单元测试也是一个坏主意......我认为最好的测试是功能黑盒测试,或集成测试,或任何你想调用它们的东西......我认为如果你正在测试 Parent,你也应该测试 Child 构造函数。我认为这是一件好事,而不是一件坏事......但如果你真的想这样做......

更新:如果你只改变Child 的实现,但你不改变它的作用,那么如果你做了功能测试,Parent 的测试就不会中断。此外,如果您有 Parent 测试,您可以对 Child 做任何您想做的事情,并且不会因为您在所有地方都嘲笑 Child 而中断,那么您怎么知道 Parent 正确使用 Child?以我的经验,在集成两个组件时最常发现大多数错误,最困难的错误,因为一个假设某事以一种合理的方式完成,另一个假设以另一种合理的方式完成(但不同的方式)。我认为您应该尽可能多地测试集成(和功能)。关于必须一次又一次地测试 Child 的属性的问题,你说的感觉就像你有重复的代码,

于 2012-07-14T00:17:25.353 回答