所以我们有一个3层Web API项目,我试图实现一些测试。
我们使用Autofac作为我们的IoC容器,以及它与Moq的集成库来实现一些单元测试。
从一些代码开始:
[TestMethod]
public void Get()
{
using (var mock = AutoMock.GetLoose())
{
// DATA LAYER
// mock some contact objects up
var contactsList = new List<CONTACT>
{
new CONTACT { CONTACT_ID = 1, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock1", SURNAME = "Mock1" },
new CONTACT { CONTACT_ID = 2, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock2", SURNAME = "Mock2" },
new CONTACT { CONTACT_ID = 3, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock3", SURNAME = "Mock3" },
new CONTACT { CONTACT_ID = 4, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock4", SURNAME = "Mock4" },
new CONTACT { CONTACT_ID = 5, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock5", SURNAME = "Mock5" },
}.AsQueryable();
var contactsSet = new Mock<DbSet<CONTACT>>();
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.Provider).Returns(contactsList.Provider);
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.Expression).Returns(contactsList.Expression);
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.ElementType).Returns(contactsList.ElementType);
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.GetEnumerator()).Returns(contactsList.GetEnumerator());
// mock that context
var mockedContext = new Mock<ExampleEntities>();
// set up context so that the contactsSet above is returned when queried.
mockedContext.Setup(c => c.CONTACTS).Returns(contactsSet.Object);
mock.Provide<IContactsRepository, ContactsRepository>(new TypedParameter(typeof(KCMEntities), mockedContext.Object));
mock.Provide<IContacts, Contacts>();
// Instantiate the controller we are performing the test on
var testController = mock.Create<ContactsController>();
// act
ContactListResult contactListResult = testController.Get();
// assert
Assert.AreEqual(true, contactListResult.Success);
Assert.AreEqual(5, contactListResult.ContactList.Count());
Assert.AreEqual("Mock1", contactListResult.ContactList.First().Forename);
Assert.AreEqual(1001, contactListResult.ContactList.First().Id); // The businesslogic layer has been doctored to add 1000 to the id...just to test it is passing through this layer..
mock.Mock<IContactsRepository>().Verify(x => x.GetContacts(), Times.Once());
}
}
我们不确定这是否真的要完成,但我们正在尝试编写测试来测试/模拟在应用程序的所有层中调用控制器的操作。
IContactsRepository和IContacts分别是数据层和业务逻辑层中的服务。
我们需要在数据层模拟dbcontext以返回我们的模拟数据,并确保autofac在解析时知道为两个服务提供哪些组件:
mock.Provide<IContactsRepository, ContactsRepository>(new TypedParameter(typeof(KCMEntities), mockedContext.Object));
mock.Provide<IContacts, Contacts>();
我们设置了在解析时将模拟上下文传递到数据层的期望。这是关于IContactsRepository的具体实现的重写构造函数(尝试了非覆盖相同的问题)。默认构造函数只是新闻一个新的dbcontext,没有params。
这似乎达到了一定程度。
在我们尝试验证之前,所有断言都没有问题:
mock.Mock<IContactsRepository>().Verify(x => x.GetContacts(), Times.Once());
这会引发错误:
"Unable to cast object of type 'Data.Repositories.ContactsRepository' to type 'Moq.IMocked``1[Data.Interfaces.IContactsRepository]'."
现在出于显而易见的原因,我们希望使用验证来验证我们所拥有的组件/服务上是否调用了某些方法。
我们尝试了许多设置变体,例如mock.Mock>IContacts>().Setup
(....等等,并没有设法解决/正确设置。
基本上我们要么在尝试像这样测试时做一些基本上有缺陷的事情,要么希望我们只是做错误的设置。
有关如何实现这一目标的任何建议吗?
提前感谢您的帮助。