I'm trying to unit test a "service layer" / "application facade layer" method. This is the method I'm trying to unit test:
// 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;
}
(Note ... I've added copious notes here for clarity. I'm not usually this chatty.)
Since this method's job is to orchestrate Domain Layer methods and Persistence Layer methods into creating an empty order, persist it, and return it as a simple DTO, I figured this was a great job for FakeItEasy ... I'll just make sure those critical methods are being orchestrated properly making sure they get called using FakeItEasy's MustHaveHappened().
So, with that in mind, here's the unit test I created:
[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);
}
In the unit test, I can't access the ACTUAL Order that was created in the method under test, I try to do the next best thing ... take the DTO version of the Order that is RETURNED by the method under test, map the DTO version of the Order back out to a new instance of the domain model Order and make sure the properties are the same before sending it to FakeItEasy's MustHaveHappened().
I've debugged the unit test and eyed up the ACTUAL Order's properties versus the FAKED Order's properties ... I assure you, they are identical. Also, I can confirm, through debugging, that the _customerRepository.Save(order) is indeed being called.
QUESTION Is .MustHaveHappened() failing because I'm essentially sending in two different instances of the Order object -- even though their properties are identical? Even though the properties are the same, does FakeItEasy need the same instance of the input parameter to ensure that the method call has happened?
Furthermore, any suggestions for how I should be testing this sort of thing (i.e., an orchestration / service / "application facade" / what-ever-you-want-to-call-it layer method)?