Let’s talk about testing. How do you test your business logic? I spent some time thinking about it and came up with three different strategies for three different ways of implementing business logic.

CRUD

As you can read here, I think CRUD is OK for small and simple applications. You have to, however, remember what is not supported in this architecture – collections of nested objects.In the previous post I’ve written about few reason why is that. Today I will show you another one, this time related to testing. You test a CRUD application by performing a business action given some fake repository object(s) and verifying (on this fake repository) if proper objects have been created, updated and deleted.

//Arrange
var orderRepo = new FakeOrderRepository();
var orderService = new OrderService(orderRepo);

//Act
orderService.CreateNewOrder(orderData);

//Assert
var order = orderRepo.Get(id);
Assert.IsNotNull(order);
Assert.AreEqual(10, order.Value);

That’s it, no rocket science. It would be much more difficult, however, if you allowed relations between objects. In such case you would have to verify also these relations. Moreover, mocking a repository if you support relations would be much harder. You would basically had to provide an in-memory database. If you stick to simple structures, you mock a repository using a single collection and your’re done.

The classic approach to DDD

This approach is basically a compilation of what you can find in the Blue Book and in Jimmy Nilsson’s book. It is Domain-Driven Design with objects mapped to database using an Object-Relational Mapper (remember — NHibernate). These objects have properties for getting their attributes and showing them on the UI. Of course there is no dumb setters. Last but not least, there are also value objects that are used to represent attributes with complex values.

So how do you test such architecture? You take advantage of persistence ignorance of good domain model. Prepare a domain object in state you want to test by invoking normal business methods. Once it is ready, call the method you want to test. Then verify the results by using property getters.

//Arrange
var customer = new Customer("Szymon", "Pobiega");
var product = new Product("Widgets", new Money(10, new Currency("USD")));
var order = new Order(orderId, customer);
order.AddLineItem(3, product);

//Act
order.AddLineItem(1, product);

//Assert
Assert.AreEqual(2, order.LineItems.Count())

Testing value objects is even simpler because of their immutability. All you need is to call a method on it (returning a new instance of a value object) and compare it with expected value.

//Arrange
var usd = new Currency("USD");
var total = new Money(10, usd);
var toSubtract = new Money(2, usd);

//Act
var result = total.Minus(toSubtract);

//Assert
var expected = new Money(8, usd);
Assert.AreEqual(expected, result);

The trick in testing classic DDD is to not depend on your ORM to do anything for you behind the scenes. The good example is many-to-many relation. In NHibernate, if you set value only on one end and persist the entities, both ends will be set magically when you get the object from database next time. But before that, for some time the object model will be inconsistent. Good practice for such relations is have code in domain model that explicitly sets both ends of particular relation.

Another gotcha is preparing the object state. Like I said, you do it by calling normal methods. The downside of this approach is something may go wrong in this phase of test and the tested method will be given different state than expected. My personal pragmatic approach is to do nothing until it hurts. If it starts to cause major problems you can think about adding Assert calls after the object has been prepared to verify if it really is in the expected state.

Event Sourced DDD

One of the reasons I really like Event Sourcing is it doesn’t cause any (known to me) problems when testing. If I was to name the ideal types of code for testing, the first one on the list would be functional code and event sourced entities would be right behind it.

The beauty of testing event sourced objects comes from the fact that state mutations and business logic are separated. You can mutate the state (by applying an event) without any business action. That’s how entities are hydrated from the event stream in the first place. There is also an inherent beauty in definition of such tests

//Arrange
var order = new Order();
order.Apply(new ItemAddedEvent(10, productId));
order.Apply(new ItemAddedEvent(5, productId));
order.Apply(new ItemRemovedEvent(2, productId));

//Act
order.AddItem(3, productId);

//Assert
order.UncommittedEvents.Contains(new ItemAddedEvent(3, productId));

Which translates to

Given these events had happened before, when this action is called, I expect these events to be published

Nice, isn’t it?

Summary

Of course you can’t always afford to create a fully-featured event sourced solution. For simple scenarios it’s an overkill. But that’s not an excuse for not having good tests. Every approach to structuring your business logic is testable, you only have to know what constraints you have to place on the code so that testing it is a pleasure, not a pain in the back.

VN:F [1.9.13_1145]
Rating: 5.0/5 (1 vote cast)
Testing strategies for business logic, 5.0 out of 5 based on 1 rating