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 を作成することになりましたが、これに基づいてテストすると、実際のインターフェイスがどのように検証されるのでしょうか?

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;
    }
}

次に、

    [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);

    }

私は正しい方法でテストに近いところにありますか?

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

4

3 に答える 3

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 });
    }
}

コントローラが特定のリポジトリ実装に依存していないことに注目してください。すべての必要性は、このリポジトリが指定されたコントラクトを実装することです。これで、単体テストで Moq などのモック フレームワークを使用して、偽のリポジトリを提供し、それを好きなように動作させて、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();
}
于 2012-04-17T17:34:05.147 に答える
0

DBContext のインターフェースを抽出して、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());
   //...

}

于 2012-04-17T17:27:00.517 に答える
0

個人的には、このための「統合テスト」を作成することを考えています。つまり、データアクセスレイヤーには、分離してテストする価値のあるロジックを含める必要がないため、実際の (ish) データベースにヒットするものです。

この場合、データベースを稼働させておく必要があります。これは、どこかで既にセットアップされている開発者データベース、またはテスト アレンジの一部として開始されたインメモリ データベースである可能性があります。

この理由は、DAL の (純粋な) 単体テストは、通常、モック フレームワークを使用できることを証明するものであり、コードにあまり自信を持たないことがわかっているためです。

単体テストにまったく慣れておらず、DAL テストに必要な環境をセットアップするのに役立つ大学が手元にない場合は、今のところ DAL のテストをやめて、ビジネス ロジックに集中することをお勧めします。費用対効果が最大になり、テストがどのように役立つかを簡単に確認できるようになります。

于 2012-04-18T12:24:58.203 に答える