web-dev-qa-db-ja.com

戻り値の型として具象クラスを持つユニットテストファクトリメソッド

だから私はファクトリクラスを持っていて、ユニットテストが何をすべきかを考えようとしています。これから 質問 返されたインターフェースが私が期待する特定の具体的なタイプであることを確認できました。

工場が具象型を返しているかどうかを確認するにはどうすればよいですか(現時点では、インターフェースを使用する必要がないため)?現在、私は次のようなことをしています。

[Test]
public void CreateSomeClassWithDependencies()
{
    // m_factory is instantiated in the SetUp method
    var someClass = m_factory.CreateSomeClassWithDependencies();

    Assert.IsNotNull(someClass);
}

これに伴う問題は、Assert.IsNotNullやや冗長なようです。

また、私のファクトリメソッドはその特定のクラスの依存関係を次のように設定している可能性があります。

public SomeClass CreateSomeClassWithDependencies()
{
    return new SomeClass(CreateADependency(), CreateAnotherDependency(),
                         CreateAThirdDependency());
}

そして、ファクトリメソッドがこれらすべての依存関係を正しく設定していることを確認したいと思います。これらの依存関係を作成するためにこれを行う他の方法はありませんpublic/internal単体テストでチェックするプロパティ? (私はテストに合うようにテスト対象を変更することの大ファンではありません)

編集:Robert Harveyの質問に答えて、私はユニットテストフレームワークとしてNUnitを使用しています(しかし、それがあまり大きな違いを生むとは思わなかったでしょう)

34
jpoh

多くの場合、状態ベースのテストに使用できるパブリックプロパティを作成しても問題はありません。はい:テストシナリオを有効にするために作成したコードですが、APIに悪影響を及ぼしますか?他のクライアントが同じプロパティを後で役立つと思うことは考えられますか?

テスト固有のコードとテスト駆動設計の間には微妙な違いがあります。テスト要件を満たす以外に可能性のないコードを導入するべきではありませんが、一般的に受け入れられている設計原則に従った新しいコードを導入することはまったく問題ありません。テストを許可しますドライブ私たちの設計-それが私たちがそれをTDDと呼ぶ理由です:)

1つ以上のプロパティをクラスに追加して、そのクラスを検査する可能性をユーザーに与えることは、私の意見では、多くの場合合理的なことなので、そのようなプロパティの導入を却下するべきではないと思います。

それとは別に、私は2番目のnaderの答えです:)

36
Mark Seemann

ファクトリが具象型を返し、ファクトリがnullではなく常に具象型を返すことを保証している場合、いいえ、テストにはあまり値がありませんtoo。これにより、時間の経過とともに、この期待に違反していないこと、および例外などがスローされないことを確認できます。

このスタイルのテストは、将来変更を加えたときに、知らないうちにファクトリの動作が変更されないことを確認するだけです。

あなたの言語がそれをサポートしているなら、あなたの依存関係のために、あなたはリフレクションを使うことができます。これは常に保守が最も簡単であるとは限らず、テストを実装に非常に緊密に結び付けます。それが受け入れられるかどうかを判断する必要があります。このアプローチは非常に脆弱になる傾向があります。

しかし、コンストラクターの呼び出し方法から、どのクラスが構築されるかを実際に分離しようとしているようです。そのような柔軟性を得るために、DIフレームワークを使用する方が良いかもしれません。

必要に応じてすべてのタイプをnew-アップすることで、多くのシーム(シームはその場所で編集せずにプログラムの動作を変更できる場所)を使用する必要がなくなります。

ただし、例を挙げれば、ファクトリからクラスを派生させることができます。次に、CreateADependency()CreateAnotherDependency()およびCreateAThirdDependency()をオーバーライド/モックします。これで、CreateSomeClassWithDependencies()を呼び出すと、正しい依存関係が作成されたかどうかをsenseすることができます。

注:「継ぎ目」の定義は、MichaelFeatherの著書「WorkingEffectivelywith Legacy Code」に基づいています。これには、テストされていないコードにテスト容易性を追加するための多くの手法の例が含まれています。非常に便利です。

25
Nader Shirazie

私たちが行っているのは、ファクトリとの依存関係を作成することです。テストの実行時に、依存性注入フレームワークを使用して、実際のファクトリをモックファクトリに置き換えます。次に、それらの模擬工場に適切な期待値を設定します。

3
Robert

あなたはいつでも反射で物事をチェックすることができます。単体テストのためだけに何かを公開する必要はありません。振り返って手を差し伸べる必要があることは非常にまれであり、それは悪いデザインの兆候かもしれません。

サンプルコードを見ると、はい、nullではないアサートは冗長に見えます。ファクトリの設計方法によっては、例外ではなく、ファクトリからnullオブジェクトを返すものもあります。

3
Sam Saffron

私が理解しているように、依存関係が正しく構築され、新しいインスタンスに渡されることをテストしたいですか?

Google guiceのようなフレームワークを使用できなかった場合は、おそらく次のようにします(ここでは、JMockとHamcrestを使用します)。

@Test
public void CreateSomeClassWithDependencies()
{
    dependencyFactory = context.mock(DependencyFactory.class);
    classAFactory = context.mock(ClassAFactory.class);

    myDependency0 = context.mock(MyDependency0.class);
    myDependency1 = context.mock(MyDependency1.class);
    myDependency2 = context.mock(MyDependency2.class);
    myClassA = context.mock(ClassA.class);

    context.checking(new Expectations(){{
       oneOf(dependencyFactory).createDependency0(); will(returnValue(myDependency0));
       oneOf(dependencyFactory).createDependency1(); will(returnValue(myDependency1));
       oneOf(dependencyFactory).createDependency2(); will(returnValue(myDependency2));

       oneOf(classAFactory).createClassA(myDependency0, myDependency1, myDependency2);
       will(returnValue(myClassA));
    }});

    builder = new ClassABuilder(dependencyFactory, classAFactory);

    assertThat(builder.make(), equalTo(myClassA));
}

(ClassAをモックできない場合は、newを使用して非モックバージョンをmyClassAに割り当てることができます)

0
lexicalscope