2

これは私のテストです:

[TestMethod]
public void TestUnitOfWork()
{
    UnitOfWork unitOfWork = new UnitOfWork();

    unitOfWork.ContactRepository.Insert(new Contact
    {
        Id = Guid.NewGuid(),
        FirstName = "Dom",
        LastName = "A",
        Email = "dominicarchual@yahoo.com"
    });

    var contacts = unitOfWork.ContactRepository.Get(x => x.FirstName == "Dominic");

    Assert.AreEqual(1, contacts.Count());
}

私が得るエラーは次のとおりです。

テスト メソッド MvcContacts.Tests.Controllers.HomeControllerTest.TestUnitOfWork が例外をスローしました: System.Data.ProviderIncompatibleException: データベースからプロバイダー情報を取得中にエラーが発生しました。これは、不適切な接続文字列を使用する Entity Framework が原因である可能性があります。内部例外で詳細を確認し、接続文字列が正しいことを確認してください。---> System.Data.ProviderIncompatibleException: プロバイダーは ProviderManifestToken 文字列を返しませんでした。---> System.Data.SqlClient.SqlException: SQL Server への接続を確立中に、ネットワーク関連またはインスタンス固有のエラーが発生しました。サーバーが見つからないか、アクセスできませんでした。インスタンス名が正しいこと、および SQL Server がリモート接続を許可するように構成されていることを確認してください。(プロバイダー: SQL ネットワーク インターフェイス、エラー:

データベースをセットアップしていません。つまり、私のコンテキストは次のようになります。

namespace MvcContacts.DAL
{
    public class ContactsContext : DbContext
    {
        public DbSet<Contact> Contacts { get; set; }
    }
}

これをデータベースにマップする方法が正確にはわかりません。しかし、モックデータを使用してテストしようとしているだけなので、まだそれを行う必要はないと考えていました。私が間違っている?

E1: これは私の作業単位クラスです。

namespace MvcContacts.DAL
{
    public class UnitOfWork : IDisposable
    {
        private ContactsContext context = new ContactsContext();
        private GenericRepository<Contact> contactRepository;

        public GenericRepository<Contact> ContactRepository
        {
            get
            {
                if (this.contactRepository == null)
                {
                    this.contactRepository = new GenericRepository<Contact>(context);
                }
                return contactRepository;
            }
        }

        public void Save()
        {
            context.SaveChanges();
        }

        private bool disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    context.Dispose();
                }
            }
            this.disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}
4

1 に答える 1

5

私が言ったように、問題はあなたが実際にあなたの内部で実際のデータベースを呼び出しているということですUnitOfWork. 私はかなり確信しています、あなたのクラスはあなたの文脈の中GenericRepository<>でラップするだけです。DbSetここで、「実際の」データベース アクセサーを作成します。

private ContactsContext context = new ContactsContext();

しかし問題は、リポジトリの概念全体を誤解していることです。作業単位は、一部のデータ ソースを抽象化したものです。抽象化を単体テストするのではなく、それに依存するいくつかの機能を単体テストする必要がありますちなみに、DbContextそれ自体はその定義による作業単位です (martinfowler.com から)。

ビジネス トランザクションの影響を受けるオブジェクトのリストを維持し、変更の書き込みと同時実行性の問題の解決を調整します。

なぜ人々はそれをそのままにしておかないのですか?そこに欠陥があるからです。例を挙げて説明しましょう。ASP.Net MVC を学習しているようですので、コントローラーを書きましょう。

public class ContactsController
{
    public ActionResult Index(int pageSize, int currentPage)
    {
         using(var db = new MvcLearningContext())
         {
             var contacts = db.Contacts
                              .Skip((currentPage - 1) * pageSize)
                              .Take(pageSize)
                              .ToList();
             return View(contacts);
         }
    }
}

ご存知かもしれませんが、MVC の大きな利点の 1 つは、コントローラー ロジックを単体テストできることです。それでは、簡単な単体テストを書いて、コントローラ アクションが指定されたページ サイズよりも多くのエントリを返さないことを確認してみましょう。

[TestMethod]
public void IndexShouldNotReturnMoreThanPageSizeResults()
{
    // arrange
    var controller = new ContactsController();

    // act
    var view = (ViewResult) controller.Index(10, 1);

    // assert  
    var Model = (IEnumerable<Contact>) view.Model;
    Assert.IsTrue(view.Model.Count() <= 10)
}

しかし待ってください...単体テストで実際のデータベースにクエリを実行したくありません。ここで EF の問題が発生しDbContextます。実際のデータベースに完全に依存します。しかし、どうすればそれを避けることができますか? UnitOfWork出番:

public class ContactsController
{
    private UnitOfWorkFactoryBase _factory { get; set; }

    public ContactsController(UnitOfWorkFactoryBase factory)
    {
        factory = _factory;
    }

    public ActionResult Index(int pageSize, int currentPage)
    {
         using(var db = _factory.Create())
         {
             var contacts = db.Contacts
                              .Skip((currentPage - 1) * pageSize)
                              .Take(pageSize)
                              .ToList();
             return View(contacts);
         }
    }
}

単体テスト コード:

[TestMethod]
public void IndexShouldNotReturnMoreThanPageSizeResults()
{
    // arrange
    var factory = new MockUnitOfWorkFactory();
    var controller = new ContactsController(factory);

    // act
    var view = (ViewResult) controller.Index(10, 1);

    // assert  
    var Model = (IEnumerable<Contact>) view.Model;
    Assert.IsTrue(view.Model.Count() <= 10)
}

MockUnitOfWorkFactory本番環境では、次のものに置き換えますUnitOfWorkFactory

UPD: ファクトリの基本的な実装:

public abstract class UnitOfWorkFactoryBase
{
    public abstract UnitOfWorkBase Create();
}

public class UnitOfWorkFactory : UnitOfWorkFactoryBase
{
    public override UnitOfWorkBase Create()
    {
        return new UnitOfWork();
    }
}

public class MockUnitOfWorkFactory : UnitOfWorkFactoryBase
{
    public override UnitOfWorkBase Create()
    {
        return new MockUnitOfWork();
    }
}

UnitOfWorkおよびMockUnitOfWorkUnitOfWorkBase 抽象クラスの実装です。

于 2013-09-17T15:35:42.627 に答える