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 を好みます (これらはファクトリテストで 1 回だけテストされます)。

誰もがより良い解決策を持っていますか?

更新: @ user1494736 の回答から: 「親をテストしている場合は、子コンストラクターもテストする必要があると思います」 . Parentを作成する別の方法がChildあり、そのテストでChildプロパティを再度テストする必要がある場合があります。一般に、メソッドが呼び出すChildたびにコンストラクターの結果をテストしたくありません。コンストラクターがコンストラクターParentのパラメーターから複雑な計算を行うとします。さまざまな組み合わせと結果をテストするためだけに、コンストラクターだけでChildいくつかのテストが必要になります。Childここで、テストChildでコンストラクターをテストし、コンストラクターの実装Parentを変更する場合、これらはChildParentテストは失敗し始めます(私が好きではないことであり、オプション2を好む主な理由でもありますが、いくつかの予約があります)

4

2 に答える 2

2

オプション2を変更して、パラメーターとして:に渡す代わりにChildFactory、のコンストラクターに渡すことができます。ParentCreateChild()

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);
    }
}

次に、別の実装で「Testable」サブクラスを作成し、それをテストします。public class TestableParent:Parent {override public Child GetsChild(int param1、string param2){//ここでやりたいことを何でもします}}

また:

public class TestableParent:Parent {Func GetsChildImplementation; public Child GetsChild(int param1、string param2){return GettingChildImplementation();をオーバーライドします。}}

ChildにIChildインターフェイスを実装させ、それを操作すると、作業が簡単になることに注意してください...

または、Moqなどのモックフレームワークを使用するだけで、「Testable」クラスがなくても同じ効果を得ることができます。var testableParent = new Mock(/*ここにParentsパラメータを配置できます*/); testableParent.Setup(parent => parent.ObtainChild(/*パラメータに一致*/)。Returns(/*何かを返す*/);

ところで:私は個人的にホワイトボックステストは非常に悪い考えだと思います...そしてほとんどの単体テストはホワイトボックステストなので、私は一般的にほとんどの単体テストも悪い考えだと思います...最高のテストは機能的なブラックボックスだと思いますテスト、統合テスト、またはそれらを呼び出したいものは何でも...親をテストしている場合は、子コンストラクターもテストする必要があると思います。それは悪いことではなく、良いことだと思います...でも、本当にやりたいのなら...

更新:変更する場合のみ子の実装ですが、それが何をするかを変更しない場合、機能テストを実行した場合、親のテストは中断されません。さらに、Childでやりたいことが何でもできるParentテストがあり、すべての場所でChildを嘲笑しているために壊れない場合、ParentがChildを正しく使用していることをどのように知っていますか?私の経験では、ほとんどのバグと最も難しいバグは、2つのコンポーネントを統合するときに最も頻繁に見つかります。これは、1つはある合理的な方法で行われ、もう1つは別の合理的な方法で行われることを前提としているためです(ただし、別の方法です)。できるだけ統合(および機能)をテストする必要があると思います。子のプロパティを何度もテストしなければならないという問題に関して、あなたが言っていることは、あなたがコードを複製したように感じます、

于 2012-07-14T00:17:25.353 に答える