// Create a new order in the database for a customer. Given a customer id,// will create a new order and return an OrderDto for use in the presentation // layer. public OrderDto CreateOrderForCustomer(int customerId) { // Find the customer var customer = _customerRepository.GetCustomerById(customerId); // Create an order and apply special logic to get it ready for use. var orderFactory = new OrderFactory(); var order = orderFactory.CreateOrder(customer); // IMPORTANT: This is what I'm trying to unit test ... _orderRepository.Save(order); order.Status = "Editing"; // Using AutoMapper to turn this into a DTO that will be returned // to the Presentation layer. The Mappings are created in the // constructor and not depicted in this code snippet. var orderDto = Mapper.Map<Order,OrderDto>(order); return orderDto; }
(注意……为了清楚起见,我在这里添加了大量的注释.我通常不会这么说.)
由于这个方法的工作是编排域层方法和持久层方法来创建一个空订单,坚持下去,并将其作为一个简单的DTO返回,我认为这对FakeItEasy来说是一个很棒的工作……我只是确保那些关键方法正在精心编排,确保使用FakeItEasy的MustHaveHappened()调用它们.
所以,考虑到这一点,这是我创建的单元测试:
[TestMethod] public void CreateOrderForCustomer_ValidCustomer_CreatesNewOrder() { // Arrange var customer = _customerRepository.GetCustomerById(1); Assert.AreEqual(0,customer.Orders.Count); // Act var orderDto = _orderEntryService.CreateOrderForCustomer(1); // Assert // Here I'm trying to make sure to re-create the order that was actually // sent into the _customerRepository.Save() ... I should be able to // simple un-map the OrderDto back to an Order,and undo the property // change. var order = Mapper.Map<OrderDto,Order>(orderDto); order.Status = "New"; A.CallTo(() => _customerRepository.GetCustomerById(1)).MustHaveHappened(); // **THIS CAUSES AN EXCEPTION** A.CallTo(() => _orderRepository.Save(order)).MustHaveHappened(); Assert.AreEqual(1,customer.Orders.Count); }
在单元测试中,我无法访问在测试方法中创建的ACTUAL Order,我尝试做下一个最好的事情…通过被测方法返回订单的DTO版本,map订单的DTO版本返回到域模型Order的新实例,并确保属性相同,然后再将其发送到FakeItEasy的MustHaveHappened().
我已经调试了单元测试并且看到了ACTUAL Order的属性而不是FAKED Order的属性……我向你保证,它们是相同的.此外,我可以通过调试确认_customerRepository.Save(order)确实被调用.
题
是.MustHaveHappened()失败,因为我实际上是在发送Order对象的两个不同实例 – 即使它们的属性是相同的?即使属性相同,FakeItEasy是否需要输入参数的相同实例才能确保方法调用已经发生?
解决方法
Is .MustHaveHappened() failing because I’m essentially sending in two different instances of the Order object — even though their properties are identical?
是. FakeItEasy将使用.Equals,它(除非你的类重写它)引用类型默认为引用相等.
(…) does FakeItEasy need the same instance of the input parameter to ensure that the method call has happened?
不.您可以像这样进行自定义参数匹配:
A.CallTo(() => _orderRepository.Save(A<Order>.That.Matches(o => o.Status == "New" && o.Id == 10 ))).MustHaveHappened();
但是,这个问题揭示了您的代码存在问题.从您的示例中可以清楚地看出,您正在将_customerRepository注入依赖项.那很棒.你为什么不对OrderFactory做同样的事情?如果它是通过接口/基类依赖注入的,那么你可以轻松地模拟(假)它并且你当前的问题不会存在.
如果你可以改变你的代码,我建议注射工厂(遵循简单的指导方针 – “没有新闻是好消息!”).如果没有,请使用自定义匹配器验证订单属性,就像我在上面的示例中所做的那样.