10

次のような方法があるとします。

public void Save(Entity data)
{
    this.repositoryIocInstance.EntitySave(data);
}

ユニットテストを書いていただけませんか?

public void TestSave()
{
    // arrange
    Mock<EntityRepository> repo = new Mock<EntityRepository>();
    repo.Setup(m => m.EntitySave(It.IsAny<Entity>());

    // act
    MyClass c = new MyClass(repo.Object);
    c.Save(new Entity());

    // assert
    repo.Verify(m => EntitySave(It.IsAny<Entity>()), Times.Once());
}

後で、メソッドの実装を変更して、次のようなより「複雑な」ことを行う場合は、次のようになります。

public void Save(Entity data)
{
    if (this.repositoryIocInstance.Exists(data))
    {
        this.repositoryIocInstance.Update(data);
    }
    else
    {
        this.repositoryIocInstance.Create(data);
    }
}

...単体テストは失敗しますが、おそらくアプリケーションを壊すことはありません...

質問

リターンタイプ*または**内部モック以外のものを変更しないメソッドでユニットテストを作成することさえわざわざする必要がありますか?

4

8 に答える 8

6

単体テストはコードのテストだけではないことを忘れないでください。それは、いつ行動が変化するかを判断できるようにすることです。

だから、些細なことを持っているかもしれません。ただし、実装が変更され、副作用が発生する可能性があります。回帰テスト スイートに教えてもらいたい。

例: 多くの場合、setter/getter は些細なことなのでテストするべきではないと言う人がいます。それらが複雑な方法だからというわけではありませんが、誰かが無知や太った指のシナリオなどで不注意に変更する可能性があるため、私は同意しません.

私が今言ったことをすべて考えると、私は間違いなく上記のテストを実装します(モックを介して、および/またはおそらく、テスト容易性を念頭に置いてクラスを設計し、ステータスなどを報告させる価値があります)。

于 2009-08-03T10:25:45.340 に答える
4

テストが実装に依存しているのは事実です。これは避けるべきものであり (実際にはそれほど単純ではない場合もあります...)、必ずしも悪いことではありません。しかし、この種のテストは、変更によってコードが壊れていなくても壊れることが予想されます

これには多くのアプローチがあります。

  • データベースに実際にアクセスするテストを作成し、状態が期待どおりに変更されたかどうかを確認します (単体テストではなくなります) 。
  • データベースを偽装してメモリ内で操作を行うテスト オブジェクトを作成し (repositoryIocInstance の別の実装)、状態が期待どおりに変更されたことを確認します。リポジトリ インターフェイスを変更すると、このオブジェクトも変更されます。しかし、あなたのインターフェースはあまり変わらないはずですよね?
  • これらすべてが高すぎると見なし、後で不必要にテストを破る可能性があるアプローチを使用してください (ただし、可能性が低くなると、リスクを冒しても問題ありません)。
于 2009-08-03T10:37:34.720 に答える
2

一般的な経験則は、おそらく壊れる可能性のあるすべてのものをテストすることです。メソッドが問題にならないほど単純である(そして十分に単純なままである)と確信している場合は、テストでそれを公開します。

2 つ目は、実装ではなく、メソッドのコントラクトをテストする必要があるということです。変更後にテストが失敗しても、アプリケーションは失敗しない場合、そのテストは正しいものをテストしていません。テストは、アプリケーションにとって重要なケースをカバーする必要があります。これにより、アプリケーションを壊さないメソッドへのすべての変更がテストに失敗しないことが保証されます。

于 2009-08-03T10:19:01.923 に答える
1

結果を返さないメソッドでも、アプリケーションの状態は変わります。この場合の単体テストは、新しい状態が意図したとおりかどうかをテストする必要があります。

于 2009-08-03T10:20:40.187 に答える
1

「あなたの単体テストは失敗するでしょうが、おそらくあなたのアプリケーションを壊すことはないでしょう」

これは、実際には、知っておくことが非常に重要です。面倒で些細なことのように思えるかもしれませんが、他の誰かがあなたのコードを保守し始めたとき、彼らは Save に非常に悪い変更を加え、(おそらく) アプリケーションを壊した可能性があります。

コツは優先順位をつけることです。

最初に重要なものをテストします。物事が遅いときは、些細なことのテストを追加します。

于 2009-08-03T10:21:30.357 に答える
1

あなたの質問に対する簡単な答えは次のとおりです。はい、そのようなメソッドを確実にテストする必要があります。

Save メソッドが実際にデータを保存することが重要だ思います。このための単体テストを作成しない場合、どうすればわかりますか?

誰かがやってきて、EntitySave メソッドを呼び出すコード行を削除しても、単体テストは失敗しません。後で、なぜアイテムが永続化されないのか疑問に思っています...

あなたの方法では、その行を削除する人は、悪意がある場合にのみ削除すると言うことができますが、問題は、単純なものは必ずしも単純なままであるとは限らず、物事が複雑になる前に単体テストを作成することです。

Save メソッドがリポジトリで EntitySave を呼び出すことは、実装の詳細ではありません。これは、予想される動作の一部であり、私がそう言うなら、非常に重要な部分です。データが実際に保存されていることを確認したい。

メソッドが値を返さないからといって、テストする価値がないわけではありません。一般に、適切なコマンド/クエリ分離 (CQS) を観察する場合、void メソッドは何かの状態を変更することが期待されます。

その何かがクラスそのものである場合もあれば、他の何かの状態である場合もあります。この場合、リポジトリの状態が変更されます。これをテストする必要があります。

これは、通常の直接出力(戻り値)ではなく、間接出力のテストと呼ばれます。

コツは、頻繁に壊れないように単体テストを作成することです。Mocks を使用する場合、誤ってOverspecified Testsを記述しやすいため、ほとんどの Dynamic Mocks (Moq など) がデフォルトでStubモードに設定されています。このモードでは、特定のメソッドを何回呼び出しても問題ありません。

これらすべて、およびその他多くのことが、優れたxUnit Test Patternsで説明されています。

于 2009-08-03T15:12:01.050 に答える
1

メソッドにアサーションがない場合、基本的には、例外がスローされないと主張しています。

public void myMethod() をテストする方法の問題にも苦労しています。テスト容易性のために戻り値を追加することにした場合、戻り値は、アプリケーションの状態について何が変化したかを確認するために必要なすべての顕著な事実を表す必要があると思います。

public void myMethod()

になる

 public ComplexObject myMethod() { 
 DoLotsOfSideEffects()
 return new ComplexObject { rows changed, primary key, value of each column, etc };
 }

そしてそうではない

public bool myMethod()  
  DoLotsOfSideEffects()
  return true;
于 2009-08-03T11:53:17.813 に答える