简体   繁体   English

单元测试无法进行工作单元测试

[英]Unit Test Fails for Unit Of Work Test

This is my 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());
}

The error I get is: 我得到的错误是:

Test method MvcContacts.Tests.Controllers.HomeControllerTest.TestUnitOfWork threw exception: System.Data.ProviderIncompatibleException: An error occurred while getting provider information from the database. 测试方法MvcContacts.Tests.Controllers.HomeControllerTest.TestUnitOfWork引发异常:System.Data.ProviderIncompatibleException:从数据库获取提供程序信息时发生错误。 This can be caused by Entity Framework using an incorrect connection string. 这可能是由于实体框架使用错误的连接字符串引起的。 Check the inner exceptions for details and ensure that the connection string is correct. 检查内部异常以获取详细信息,并确保连接字符串正确。 ---> System.Data.ProviderIncompatibleException: The provider did not return a ProviderManifestToken string. ---> System.Data.ProviderIncompatibleException:提供程序未返回ProviderManifestToken字符串。 ---> System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. ---> System.Data.SqlClient.SqlException:建立与SQL Server的连接时发生与网络相关或特定于实例的错误。 The server was not found or was not accessible. 服务器未找到或无法访问。 Verify that the instance name is correct and that SQL Server is configured to allow remote connections. 验证实例名称正确,并且已将SQL Server配置为允许远程连接。 (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified) (提供者:SQL网络接口,错误:26-指定服务器/实例时出错)

I don't have any database set up; 我没有任何数据库设置; ie my context looks like this: 即我的上下文是这样的:

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

I don't know exactly how to map this to my database; 我不知道如何将其映射到数据库。 but, I was thinking that I wouldn't have to do that yet since I am just trying to test using mock data. 但是,我以为我还不需要这样做,因为我只是尝试使用模拟数据进行测试。 Am I wrong? 我错了吗?

E1: This is my unit of work class. 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);
        }
    }
}

As i said, the problem is that you are actually calling real database inside your UnitOfWork . 如我所说,问题在于您实际上是在UnitOfWork内部调用真实数据库。 I'm pretty sure, your GenericRepository<> class just wraps DbSet inside your context. 我很确定,您的GenericRepository<>类只是将DbSet包装在您的上下文中。 Here is where you create the 'real' database accessor. 在这里创建“真实”数据库访问器。

private ContactsContext context = new ContactsContext();

But the problem is you misunderstand the whole concept of repositories. 但是问题是您误解了存储库的整个概念。 Unit of work is an abstraction of some data source. 工作单元是某些数据源的抽象 You should not unit test the abstraction, instead you should unit test some functionality which depends upon it. 不应该对抽象进行单元测试,而应该对依赖于抽象的某些功能进行单元测试。 By the way, DbContext itself is a Unit of work by that definition (from martinfowler.com): 顺便说一句, DbContext该定义, DbContext本身就是工作单元(来自martinfowler.com):

Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. 维护受业务交易影响的对象列表,并协调更改的写出和并发问题的解决。

Why don't people just leave it as it is? 人们为什么不就这样保留它呢? Because there is a flaw in it. 因为它有缺陷。 Let me explain by example. 让我举例说明。 Seems like you're learning ASP.Net MVC so let's write some controller: 似乎您正在学习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);
         }
    }
}

As you may know, one of the great advantages of MVC is the ability to unit test controller logic. 如您所知,MVC的一大优点是可以对控制器逻辑进行单元测试。 So, let's try to write a simple unit test to make sure out controller action doesn't return more entries than the given page size: 因此,让我们尝试编写一个简单的单元测试,以确保控制器操作不会返回超过给定页面大小的条目:

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

But wait... We do not want to query the real database in the unit test. 但是,等等...我们不想在单元测试中查询真实的数据库。 Here comes the problem with EF's DbContext : it completely depends on real database. EF的DbContext出现了问题:它完全取决于实际数据库。 But how can we avoid that? 但是,如何避免这种情况呢? UnitOfWork comes in play: 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);
         }
    }
}

unit test code: 单元测试代码:

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

and in production you replace MockUnitOfWorkFactory with UnitOfWorkFactory 在生产中,您将MockUnitOfWorkFactory替换为UnitOfWorkFactory

UPD: basic implementation of factories: 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 and MockUnitOfWork are implementations of UnitOfWorkBase abstract class. UnitOfWorkMockUnitOfWork是UnitOfWorkBase抽象类的实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM