2016-12-04 5 views
0

これは私の初めての単体テストテストです。ちょっと質問があります。私は私のサービスをテストするためにメモリデータベースを使用しています。私は正しくそれをやっているのだろうかと思っています。私の最初の質問は、私はすべてのサービスコールで複数のアサーションが必要なのでしょうか?私はInsertProductのために断言する必要がありますか?次に、すべてのサービスコールでコンテキストの新しいインスタンスを使用することについてこれをテストしていますか?複数のアサートが必要ですか? xUnitテスト

[Fact] 
public void ProductService_DeleteProduct_Test() 
{ 
    // arrange 
    var options = new DbContextOptionsBuilder<ApplicationDbContext>() 
     .UseInMemoryDatabase(databaseName: "ProductService_DeleteProduct_Test") 
     .Options; 

    var product = new Product() { Id = Guid.NewGuid(), Name = "Product"}; 

    // act 
    // insert 
    using (var context = new ApplicationDbContext(options)) 
    { 
     var service = new Service(context); 
     service.ProductService.InsertProduct(product); 
    } 

    // delete 
    using (var context = new ApplicationDbContext(options)) 
    { 
     var service = new Service(context); 
     service.ProductService.DeleteProducts(new List<Guid> { product.Id }); 
    } 

    // assert 
    using (var context = new ApplicationDbContext(options)) 
    { 
     var service = new Service(context); 
     Assert.Equal(0, service.ProductService.GetAllProducts().Count); 
    } 
} 

答えて

1

私はあなたのテストの構造に異議を唱えます。すなわち、基礎となるデータベースを準備するためにサービス(実動コード)を使用しています。また、アサーションを作成するためにプロダクションコードを使用しています。

プロダクションコードの一部が正しくない場合、このテストは失敗します。ただし、このテストは、削除機能が正しく機能していると主張するように設計されています。

したがって、私は次の方法で試験全体を書き換えます。このように

[Fact] 
public void ProductService_DeleteProduct_Test() 
{ 
    // arrange 
    var options = new DbContextOptionsBuilder<ApplicationDbContext>() 
     .UseInMemoryDatabase(databaseName: "ProductService_DeleteProduct_Test") 
     .Options; 

    var product = new Product() { Id = Guid.NewGuid(), Name = "Product"}; 

    // Insert object using other means, i.e. direct INSERT statement 

    // act 
    using (var context = new ApplicationDbContext(options)) 
    { 
     var service = new Service(context); 
     service.ProductService.DeleteProducts(new List<Guid> { product.Id }); 
    } 

    // assert 
    // Execute SELECT COUNT(*) instruction to fetch previously existing row 
    Assert.Equal(0, rowsCount); 
} 

、あなただけのテストの作用部で生産コードに触れます。これは、サービスオブジェクトを使用してデータベースからオブジェクトを削除する部分です。

以降のアサーションは、スレーブcountに対して行われ、生のSELECTステートメントがストレージ上で直接実行された結果としてフェッチされます。

最終的には、実際にテスト対象のメソッドであるDeleteProductsメソッドを除いて、テストのどの部分もプロダクションコードの正確さに依存していないということです。

あなたの疑問に対する答えは、このテストで書くアサーションが1つだけということです。

+0

私はあなたの質問を受け入れる前に、私のテストを修正するためにエンティティフレームワークを使用しています。私は 'context.Database.ExecuteSqlCommand'または' context.Database.SqlQuery'を使う必要があります。 Data.SqlClient; ' – kram005

+0

おそらくあります。しかし、過去に私がテスト・プロジェクトで別の接続文字列を持っていたテスト・スイートがあったことを教えてください。そして未処理のSQLを実行するために裸の 'SqlCommands'を使いました。これにより、テストとプロダクションコードの間の最後の接続さえも取り除かれます。とにかく、これは単なるディテールです。あなたは既存の 'DbContext'を使ってうまくいくでしょう。 –

+0

メモリデータベースにefを使用していて、sqlcommandを使用してメモリデータベースで行う方法がわからないため、dbcontextを使用する必要があります – kram005

1

あなたの最初の質問に答えると、いいえと言います。これは単体テストなので、特に削除をテストしています。私はあなたが削除をテストしたい状態にシステムを持っているので、セットアップの挿入部分を考慮する。 Zoran Horvatのポイントには、可能であれば、サービス自体以外の何らかの手段でデータベースに行を置くことができます。

2番目の質問に答えるには、サービスを3回新しくしているブロックを3つ使用する必要はないようです。私は、SUTの1つのインスタンス、またはサービスを利用して、insert、delete、assertを同じものに入れて使用します。

ただし、データベースに行が必要な複数のテストがある場合、すべてのテストで手で呼び出せる[SetUp]属性を持つSetUpメソッドにインサートを移動することを検討してください。その場合、コンテキストの複数のインスタンスを使用しています。

関連する問題