簡體   English   中英

單元測試無法進行工作單元測試

[英]Unit Test Fails for Unit Of Work Test

這是我的測試:

[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:從數據庫獲取提供程序信息時發生錯誤。 這可能是由於實體框架使用錯誤的連接字符串引起的。 檢查內部異常以獲取詳細信息,並確保連接字符串正確。 ---> System.Data.ProviderIncompatibleException:提供程序未返回ProviderManifestToken字符串。 ---> System.Data.SqlClient.SqlException:建立與SQL Server的連接時發生與網絡相關或特定於實例的錯誤。 服務器未找到或無法訪問。 驗證實例名稱正確,並且已將SQL Server配置為允許遠程連接。 (提供者:SQL網絡接口,錯誤:26-指定服務器/實例時出錯)

我沒有任何數據庫設置; 即我的上下文是這樣的:

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

如我所說,問題在於您實際上是在UnitOfWork內部調用真實數據庫。 我很確定,您的GenericRepository<>類只是將DbSet包裝在您的上下文中。 在這里創建“真實”數據庫訪問器。

private ContactsContext context = new ContactsContext();

但是問題是您誤解了存儲庫的整個概念。 工作單元是某些數據源的抽象 不應該對抽象進行單元測試,而應該對依賴於抽象的某些功能進行單元測試。 順便說一句, DbContext該定義, 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的一大優點是可以對控制器邏輯進行單元測試。 因此,讓我們嘗試編寫一個簡單的單元測試,以確保控制器操作不會返回超過給定頁面大小的條目:

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

UnitOfWorkMockUnitOfWork是UnitOfWorkBase抽象類的實現。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM