use*_*388 2 c# asp.net-mvc unit-testing unit-of-work
这是我的测试:
[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());
}
Run Code Online (Sandbox Code Playgroud)
我得到的错误是:
测试方法MvcContacts.Tests.Controllers.HomeControllerTest.TestUnitOfWork引发异常:System.Data.ProviderIncompatibleException:从数据库获取提供程序信息时发生错误.这可能是由实体框架使用不正确的连接字符串引起的.检查内部异常以获取详细信息,并确保连接字符串正确.---> 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; }
}
}
Run Code Online (Sandbox Code Playgroud)
我不确切知道如何将其映射到我的数据库; 但是,我想我不必这样做,因为我只是试图使用模拟数据进行测试.我错了吗?
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
正如我所说,问题是你实际上是在你的内部调用真正的数据库UnitOfWork.我很确定,你的GenericRepository<>课程只是DbSet在你的背景中.您可以在此处创建"真正的"数据库访问器.
private ContactsContext context = new ContactsContext();
Run Code Online (Sandbox Code Playgroud)
但问题是你误解了存储库的整个概念.工作单元是一些数据源的抽象.您不应该对抽象进行单元测试,而应该对依赖于它的某些功能进行单元测试.顺便说一句,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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可能知道,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)
}
Run Code Online (Sandbox Code Playgroud)
但是等等......我们不想在单元测试中查询真实数据库.这就是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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
单元测试代码:
[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)
}
Run Code Online (Sandbox Code Playgroud)
在生产中你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();
}
}
Run Code Online (Sandbox Code Playgroud)
UnitOfWork并且MockUnitOfWork是UnitOfWorkBase抽象类的实现.
| 归档时间: |
|
| 查看次数: |
1500 次 |
| 最近记录: |