Monday, April 12, 2010

Testing with Domian Driven Design

DDD is a hairy beast. It feels like everyone has a slightly different opinion of what it is. I personally thought I had a pretty good grasp on it until I caught up with Udi Dahan earlier this year where he (as per usual) completely turn my understanding on its head.
That being said I believe the way I am approaching the current project is in line with what I believe the majority of people believe DDD is, correctly or otherwise.
Key aspects in this project that help make it resemble a DDD domain are:
  • The level of understanding the devs have and the ubiquitous language that is in use and they way it is constantly evolving amoungts all team memebers, including our SMEs/Users
  • The general structure of the code - repositories, services, aggregate roots comprised of entities and value types etc etc
To be honest one could argue, that like most DDD projects, it is just clean OO coding. It is; that doesn't mean that there isn't something that others could glean of of the project, such as what we are doing to keep things clean and help with creating nice tests.

First and foremost follow TDD and BDD if possible
If you have a good understanding of the component you are building then writing explicit specifications should be easy. Do this with your BA and SME make sure the tests are not shallow low value tests. Investigate what BDD is and see if one of the many framework fit your teams needs. Personally I am still using xUnit frameworks and am creating elaborate contexts on which I make assertions. My fixture set ups can sometimes be complex but my test methods are very clear & clean and can often just be one line assert statements. I have not found a BDD framework that sits well with me in the .Net world like Cucumber does in my Rails development, so I will continue down this path till I find something I like better than explicit and somewhat elaborate xUnit styled tests.

The domain should remain as pure as humanly possibly
Anything that is public should have a good reason for being so. Unfortunately most developers use public as their default. This is bad practice as it produces a very non intuitive API. Use access to help show the other developers how you intended the API to be used.
Typically, IMO, this means protected virtual by default. (I'm an NH fanboi). Also restrict what you return. I personally don't like the idea of returning child entities from parent entities especially aggregate roots. What is the consumer going to do with these entities? Most of the time a value type or projection is more appropriate and helps keep the line of ownership clean.

Modifying the domain to be more testable should be frowned upon, especially if it confused the API
This ties into the first point. NB : There are concession that I make that I can live with (I will cover them soon) but I believe these do not negatively affect my domain and make life easier and my intentions more explicit to the next developer

Use subclass fakes to increase accessibility for testing
I often see people making fields, properties or methods public so tests can call them. Please don't do this. If these are private definitely do not expose them. This is an indication that you are not doing TDD. Private methods generally only come from refactoring once your test pass so testing private methods is a sure sign you are not doing TDD. Internals sometimes may warrant being tested. This is OK as we can make internals visible to the test project. I generally don't mind doing this as I put this in the AssemblyInfo file which is recreated in my deployment anyway so there is no dodgy test orientated code in my final deployed assembly. I must say that I don't really do this a lot and i think people use this as a crutch. True TDD generally will not warrant a lot of this, However i find myself doing it a bit in my domain, possibly out of fear more than any really rational reason.
If you really need to make something accessible to test and don't want to expose it fully then create a fake that inherits from the class and override the accessor in that class. Sometimes I do this to check things like IDs. Again question whether this is really needed, have faith in the TDD process!

Allow hooks in the domain for creating child objects
One thing I have used in the current project is protected factory method to create child entities. By opening up these one liners I can override the method in a fake sub class to create a fake subclass of the child: eg OrderFake will create and return an OrderLineFake in its CreateOrderLine method as opposed to the Order creating and returning an OrderLine in its CreateOrderLine.

When using fakes of real domain objects make sure you are not hiding any of the real object functionality. The fakes should be as plain as possible. Adding additional logic will surely corrupt your tests. One thing to watch for is to make sure your fakes implement the same ctors as their parent and call into those ctors. Failure to do this will create a big PITA :)

Next post I will talk about some issues and traps that we as a team have managed to fall into. Most of them have been things when consciously question know we shouldn't do but have managed to creep into the solution, hopefully we can help you avoid repeating our mistakes!

5 comments:

roundcrisis said...

Hi Rhys:

Nice post, I m suprised there are
On the "The domain should remain as pure as humanly possibly" funny, I agree with you on the part that the API should be clean, but I cant come to terms with a way to do this, so far I ve been using internals, its a lot of pain, but so far so good.
I ll have to try the way you are suggesting with protected virtual.
Cheers

Andrea

roundcrisis said...

Hi Rhys:

Nice post, I m suprised there are
On the "The domain should remain as pure as humanly possibly" funny, I agree with you on the part that the API should be clean, but I cant come to terms with a way to do this, so far I ve been using internals, its a lot of pain, but so far so good.
I ll have to try the way you are suggesting with protected virtual.
Cheers

Andrea

RhysC said...

I typically find the Domain becames "dirty" when i am not following a TDD approach, so I really try to follow a TDD approach!
If you are testing from the prespective of an aggregate root then the reduced access should be fine; it highlights how you are to use the API. Opening up the domain for testing is usually a crutch, you do not need a fixture per production class. You are better off having higher level fixtures that focus on a scenarios; by creating complete valid and invalid scenarios for those fixtures you will get the coverage you need.

Billy Stack said...

Very good article but a few points I would like to make:

We use TDD approach but *always* declare every class as public - it works perfect for us and does not pollute the domain IMO. As dependency injection is used throughout our code, our domain is easily testable and mockable, but API meaning is not cluttered as "ubiquitous language" ensures a concise and clear meaning.

If functionality in a classes private method is hard to test, it is usually a pointer to a code/test smell and your class should be looked at. In fact it often points to the fact that the class is doing too much and the private method is itself extracted into a class of its own!

Finally, Im also a fan of NHibernate but unfortunately its use in a DDD application violates "persistence ignorance" as you must include an empty ctor (it can be private) and virtual properties (if using lazy loading)

RhysC said...

Declaring every class as public is not the same as having every method or property as public. Most of my domain types are public (i have non public inner classes for many value types), however they may have little or no public methods or properties: You should not be able to directly manipulate a child of an aggregate so you should enforce this with a descriptive API.
With reagrds to mocking, it has no place in domain entities or value types. I have been in discussions a couple of years ago where people would say its ok to have a service injected into you entities. I couldn't disagree more. After several years of DDD I have not been able to think of a place where the domain could not remain clean. Typically this is via static domain entity events that a broker can forward on to a service.
I am currently working on a large code base with DI being used in the enities. Its all bad and readily fixed. The problem is in the tests as we are effectively stubbing out real behaviour, when you should only be stubbing out interactions. Mocks at a service level im OK with mocks in an entity is not good.

With regard to persistance ignorance: if you want to have real PI then look at the many NoSQl options such as MongoDb, CouchDb or Ayendes new RavenDb. They have a very nice fit with DDD but you wont win any friends in your DBA department ;)