2012-04-17 14 views
0

私はユニットテスト(TDD)で自分の足を濡らしています。私はテストするために探している基本的なリポジトリパターンを持っています。この段階で私は自分のドメインをテストしていますが、コントローラとビューについて心配する必要はありません。それを簡単に保つためにデモプロジェクトがあります。リポジトリパターンを使用している場合のドメインのテスト

クラス

public class Person 
{ 
    public int PersonID { get; set; } 
    public string Name{ get; set; } 
} 

インタフェース

public interface IPersonRepository 
{ 
    int Add(Person person); 
} 

コンクリート

public class PersonnRepository : IPersonRepository 
{ 

    DBContext ctx = new DBContext(); 

    public int Add(Person person) 
    { 
     // New entity 
     ctx.People.Add(person); 
     ctx.SaveChanges(); 
     return person.id; 

    } 
} 

私は私のテストプロジェクトにNUnitのとMOQを追加し、適切に機能をテストする方法を知りたいきました。

私はそれが正しいとは確信していませんが、いくつかのブログを読んだ後、私はFakeRepositoryを作成しました。しかし、これに基づいてテストすれば、私の実際のインターフェースをどのように検証しますか?その後、

[Test] 
    public void Creating_A_Person_Should_Return_The_ID() 
    { 

     FakePersonRepository repository = new FakePersonRepository(); 

     int id = repository.Add(new Person { Name = "Some Name" }); 

     Assert.IsNotNull(id); 

    } 

でテスト

public class FakePersonRepository 
{ 

    Dictionary<int, Person> People = new Dictionary<int, Person>(); 

    public int Add(Person person) 
    { 
     int id = People.Count + 1; 
     People.Add(id, person); 
     return id; 
    } 
} 

は正しいマナーでの試験に私はどこにも近いのですか?

今後、名前を渡さないなどのエラーが発生するなどのテストをしたいと考えています。

答えて

0

あなたはそれのためのインタフェースを抽出することによって、あなたのDBContextは、注射行う必要があります。

public interface IDBContext{ 
    IList<Person> People {get;} // I'm guessing at the types 
    void SaveChanges(); 
    //  etc. 
} 

次に、あなたの具象クラスにそれを注入:

public class PersonRepository : IPersonRepository 
{ 

    IDBContext ctx; 

    public PersonRepository(IDBContext db) { 
     ctx = db; 
    } 

    public int Add(Person person) 
    { 
     // New entity 
     ctx.People.Add(person); 
     ctx.SaveChanges(); 
     return person.id; 

    } 
} 

あなたのテストはその後、次のようになります。

[Test] 
public void Creating_A_Person_Should_Return_The_ID() 
{ 

    Mock<IDBContext> mockDbContext = new Mock<IDBContext>(); 
    // Setup whatever mock values/callbacks you need 

    PersonRepository repository = new PersonRepository(mockDbContext.Object); 

    int id = repository.Add(new Person { Name = "Some Name" }); 

    Assert.IsNotNull(id); 

    // verify that expected calls are made against your mock 
    mockDbContext.Verify(db => db.SaveChanges(), Times.Once()); 
    //... 

}

+0

ありがとうございます。私は理解が間違っているか、私の例が混乱しているかもしれません。この場合、dbContextは、EFとデータベースで作業できるようにするEF DbContextを表すことを意図していました。したがって、あなたが表示するプロパティはありません。 私の具象PersonRepositoryはデータベースに書き込むためにEFにアクセスしますが、これは単体テストではなく、後で統合テストされたことを示しています。 – stevejgordon

+0

私は、私が私のテストを意味していることを保証したいと思っていた同じインタフェースを実装している(私のメソッドを確認するためのdbを取るための)インメモリ版が必要だと推測していました。私はMVC、EFコード、TDDのコンセプトの両方に慣れていないので少し失われていると思います。 – stevejgordon

+0

インターフェイスではなく、具体的な実装をテストします。これを行うには、実装をDBContextから切り離す必要があります。その理由は、PersonRepository具象クラスに注入できるインターフェイスを抽出することを提案したからです。 単体テストの鍵は、クラス内のコードを分離できるように、すべての依存関係を注入可能にすることです。単体テストでは、依存関係の模擬バージョンを渡します。あなたの場合、これは上記のようにDBContextを注入できる必要があることを意味します。 –

4

正しいマナーのどこかでテストに近づいていますか?

私はあなたがそうではないと恐れています。インターフェイスを持つという考えは、コントローラなどのリポジトリを使用する他のコードを切り離し、単体で単体テストできるようにすることです。コントローラは、特定のリポジトリの実装に依存しない方法

public class PersonController : Controller 
{ 
    private readonly IPersonRepository _repo; 
    public PersonController(IPersonRepository repo) 
    { 
     _repo = repo; 
    } 

    [HttpPost] 
    public ActionResult Create(Person p) 
    { 
     if (!ModelState.IsValid) 
     { 
      return View(p); 
     } 

     var id = _repo.Add(p); 
     return Json(new { id = id }); 
    } 
} 

お知らせ:それでは、あなたはユニットテストしたい以下のコントローラを持っていると仮定してみましょう。すべてのニーズは、このリポジトリが指定された契約を実装することです。今、私たちは偽のリポジトリを提供するために、ユニットテストで、このような部品番号としてモックフレームワークを使用して、我々はCreateアクションで2つの可能なパスをテストするために好きなようにそれを動作させることができます:

[TestMethod] 
public void PersonsController_Create_Action_Should_Return_View_And_Not_Call_Repository_If_ModelState_Is_Invalid() 
{ 
    // arrange 
    var fakeRepo = new Mock<IPersonRepository>(); 
    var sut = new PersonController(fakeRepo.Object); 
    var p = new Person(); 
    sut.ModelState.AddModelError("Name", "The name cannot be empty"); 
    fakeRepo.Setup(x => x.Add(p)).Throws(new Exception("Shouldn't be called.")); 

    // act 
    var actual = sut.Create(p); 

    // assert 
    Assert.IsInstanceOfType(actual, typeof(ViewResult)); 
} 


[TestMethod] 
public void PersonsController_Create_Action_Call_Repository() 
{ 
    // arrange 
    var fakeRepo = new Mock<IPersonRepository>(); 
    var sut = new PersonController(fakeRepo.Object); 
    var p = new Person(); 
    fakeRepo.Setup(x => x.Add(p)).Returns(5).Verifiable(); 

    // act 
    var actual = sut.Create(p); 

    // assert 
    Assert.IsInstanceOfType(actual, typeof(JsonResult)); 
    var jsonResult = (JsonResult)actual; 
    var data = new RouteValueDictionary(jsonResult.Data); 
    Assert.AreEqual(5, data["id"]); 
    fakeRepo.Verify(); 
} 
+0

お返事ありがとうございます。私はこの段階でコントローラとビューを必要とせずにドメイン(CRUD +より複雑なサービス)をテストできるとは思っていましたが、私はドメインモデルとインターフェイスをテストして作業して、出力を心配する前にまずビジネス要素の設計を動かすことを望んでいました。これは不可能ですか? – stevejgordon

0

私は個人的に思いますこのための「統合テスト」、つまり、実際の(ish)データベースにヒットするものを見てみましょう。データアクセスレイヤーは、価値のある分離したテストを行うロジックを含んではいけません。

この場合、データベースを起動して実行する必要があります。これは、開発者データベースが既にどこかに設定されているか、テストの手配の一部として開始されたインメモリデータベースである可能性があります。

DALの(純粋な)単体テストが一般的にモックフレームワークを使用することができる証拠となり、少しでもコードに自信を持たせることになります。

単体テストの初心者で、DALテストに必要な環境をセットアップするためのカレッジを持っていない場合は、今すぐDALをテストしたままにして、ビジネスロジックに集中することをお勧めします。これは、あなたの借金に最も大きな影響を及ぼし、テストがどのように役立つかをより簡単に確認できるようにする場所です。

関連する問題