6

私はこれに対する良い解決策を見つけようとしてきましたが、運がなかったので、正しいキーワードを探していないか、最初から間違ったことをしているので、問題は実際には存在しないはずです.

明確化のための更新:これを統合テストとしてではなく単体テストとして機能させたいので、これをデータベースにヒットさせたくありませんが、単体テストで EF が変更を保持するときに行われた関連付けをモックしたいと考えています。

元の質問:

次のようなサービス メソッドをテストしているとします。

[Test]
public void AssignAuthorToBook_NewBookNewAuthor_SuccessfullyAssigned()
{
  IBookService service = new BookService();

  var book = new Book();

  var Author = new Author() { Id = 123 };

  service.AssignAuthorToBook(book, author);

  Assert.AreEqual(book.AuthorId, 123);
}

AssignAuthorToBookここで、コードを使用して実際に機能するため、このテストが失敗したとしましょう。エンティティを割り当てているためですbook.Author = author;AuthorIdこれがコンテキストで Entity FrameworkSaveChanges()メソッドを使用して永続化されると、エンティティが関連付けられ、ID が関連付けられます。ただし、上記の例では、Entity Framework のロジックは適用されていません。私が言っているのは、一度SaveChanges()呼び出されたコードは機能しますが、単体テストは失敗するということです。

この単純な例では、コードの直前にテストを記述したばかりで、簡単に修正できるため、テストが失敗した理由がすぐにわかるでしょう。しかし、より複雑な操作や、エンティティの関連付け方法を変更する可能性のある将来の変更により、テストは中断されるが機能は中断されない可能性がある操作の場合、単体テストはどのように行うのが最善でしょうか?

私の考えは次のとおりです。

  • サービス レイヤーは永続レイヤーを認識しないようにする必要があります。単体テストでデータ コンテキストをモックして、それが機能する方法をモックする必要がありますか? 関連付けを自動的に結び付ける簡単な方法はありますか (つまり、エンティティが使用されている場合Idは正しいエンティティに割り当てId、エンティティが使用されている場合は正しいエンティティを割り当てます)。
  • それとも、テストを少し異なる方法で構成する必要がありますか?

私が継承した現在のプロジェクトに存在するテストは、上記の例のように機能しますが、アプローチに何か問題があり、おそらく一般的な問題に対する簡単な解決策を見つけることができなかったことが気になります。データ コンテキストはモック化する必要があると思いますが、関連付けを動的に作成するには、モックに多くのコードを追加する必要があるようです。これはすでに解決されていますか?

更新:これらは私がこれまでに見つけた最も近い答えですが、私が求めているものとはまったく異なります. 私はEF自体をテストしたくありません。リポジトリにアクセスするサービスメソッドをテストするためのベストプラクティスは何だろうと思っただけです(直接または同じコンテキストを共有する他のリポジトリを介してナビゲーションプロパティを介して)。

Entity Framework のナビゲーション プロパティ インテリジェンスをモックするにはどうすればよいですか?

モックされたデータ コンテキストと外部キー/ナビゲーション プロパティ

テストするEntity Framework 4.1の偽のDbContext

ADO.NET モッキング コンテキスト ジェネレーターの使用時にナビゲーション プロパティが設定されない

これまでの結論:これは単体テストでは不可能であり、実際の DB との統合テストを使用してのみ可能です。ナビゲーション プロパティを動的に関連付けるコードを作成することはできますが、モック データ コンテキストが実際のコンテキストを完全に複製することはありません。完璧ではないにしても、ナビゲーション プロパティを自動的に関連付けることができるソリューションがあれば幸いです(ユニット テストの成功の性質は、とにかく機能を保証するものではありません)。モッキング コンテキスト ジェネレーターは近づいていますが、実装で部分クラスを使用して機能が追加された場合に備えて、すべてのエンティティのモック バージョンが必要になるようです。

4

7 に答える 7

5

特にEFへの暗黙の依存関係のために、いくつかの依存関係の使用を意味するテストの結果を期待しており、おそらくユニットテストとして認定されていないと主張します。

ここでの考え方は、BookService が EF に依存していることを認識している場合は、モックを使用して、それが正しく相互作用することを主張する必要があるということです。残念ながら、EF はモックされることを好まないようです。そのため、いつでもリポジトリの下に置くことができます。 、Moq を使用してそのテストを作成する方法の例を次に示します。

[Test]
public void AssignAuthorToBook_NewBookNewAuthor_CreatesNewBookAndAuthorAndAssociatesThem()
{
  var bookRepositoryMock = new Mock<IBookRepository>(MockBehavior.Loose);
  IBookService service = new BookService(bookRepositoryMock.Object);
  var book = new Book() {Id = 0}
  var author = new Author() {Id = 0}; 

  service.AssignAuthorToBook(book, author);

  bookRepositoryMock.Verify(repo => repo.AddNewBook(book));
  bookRepositoryMock.Verify(repo => repo.AddNewAuthor(author));
  bookRepositoryMock.Verfify(repo => repo.AssignAuthorToBook(book, author));
}

設定されている id は、統合テストを使用するものですが、EF が Id の設定に失敗することを心配する必要はないと主張します。 .net フレームワークは、本来の機能を果たします。

過去にインタラクション テストについて書いたことがあります (このシナリオでは正しい方法だと思います。BookService とリポジトリの間のインタラクションをテストしています) 。 /2012/11/20/interaction-testing-fakes-mocks-and-stubs/

于 2013-08-21T09:01:28.280 に答える
1

あなたの質問は次のとおりです。データベース操作をモックする場合、AssignAuthorToBook の正しい機能をテストできません。このクラスは Entity Framework と高度に結合されており、動作が変化するためです。

だから私の解決策:クラスを分離します(DAO、すべてのデータベース操作とのインターフェースを使用)。SetBook / GetBookIdの関数を使用するため、AssignAuthorToBookは簡単にテストできます。そして、データベースをテストする操作(SetBook / GetBookId)のテストを行います(わかりました、それは明確な単体テストではありませんが、あなたの質問はまさにこれです:データベース操作をテストできるのは誰ですか?別のテスト。

于 2013-08-20T18:46:13.660 に答える
1

それはあなたが持っているように機能することは決してありません。データ コンテキストはサービス レイヤーの舞台裏でラップされ、BookID の関連付けがローカル変数で更新されることはありません。

EF を使用して何かに TDD を実行した場合、通常、すべての EF ロジックをある種の DAO にラップし、エンティティの CRUD メソッドを用意します。

次に、次のようなことができます。

[Test]
public void AssignAuthorToBook_NewBookNewAuthor_SuccessfullyAssigned()
{
  IBookService service = new BookService();

  var book = new Book();
  var bookID = 122;
  book.ID = bookId;
  var Author = new Author() { Id = 123 };

  service.AssignAuthorToBook(book, author);

//ask the service for the book, which uses EF to get the book and populate the navigational properties, etc...
  book = service.GetBook(bookID)

  Assert.AreEqual(book.AuthorId, 123);
}
于 2013-08-14T15:51:42.717 に答える
0

「AssignAuthorToBook」が期待どおりに機能することをテストしたい場合は、完全に DB を無視して行うことができます。Book オブジェクトのモックを作成し、正しいセッターが正しい値で呼び出されたことを確認します。永続性に関連するものはすべてスタブ化する必要があります。セッターまたはメソッドが呼び出されたことを確認するには、MoqまたはRhinoMocksを使用できます。

例はここにあります: Rhino Mocks: AAA Synax: Assert property was set with a given type

代替の期待値を検証する方法の例を次に示します: RhinoMocks を使用して、いくつかのメソッドの 1 つが呼び出されたことをどのようにアサートできますか?

于 2013-08-15T09:57:13.653 に答える
0

私が間違っているかもしれませんが、私が知る限り、永続化レイヤーがなくてもドメイン モデルは一貫している必要があります。

したがって、この場合、サービスは、プロパティをデータベースに永続化する前であっても、プロパティ AuthorID が割り当てられた作成者クラスと等しいことを確認する必要があります。または、Book AuthorID プロパティ ゲッターは、サービスによって割り当てられた内部の Author クラス ID プロパティからその情報を取得します。

public class Book {
    public Author Author { get; set; }
    public int AuthorId { 
        get { return Author.ID; }
        set { Author.ID = value; }
    }
}

public class Author {
    public int Id { get; set; }
}

public class BookService {
    public void AssignAuthorToBook(Book book, Author author)
    {
        book.Author = author;
    }
}
于 2013-08-23T12:56:44.183 に答える