views:

45

answers:

2

We are a few months into a green-field project to rework the Logic and Business layers of our product. By utilizing MEF (dependency injection) we have achieved high levels of code coverage and I believe that we have a pretty solid product. As we have been working through some of the more complex logic I have found it increasingly difficult to unit test.

We are utilizing the CompositionContainer to query for types required by these complex algorithms. My unit tests are sometimes difficult to follow due to the lengthy mock object setup process that must take place, just right, to allow for certain circumstances to be verified. My unit tests often take me longer to write than the code that I'm trying to test.

I realize this is not only an issue with dependency injection but with design as a whole. Is poor method design or lack of composition to blame for my overly complex tests? I've tried base classing tests, creating commonly used mock objects and ensuring that I utilize the container as much as possible to ease this issue but my tests always end up quite complex and hard to debug. What are some tips that you've seen to keep such tests concise, readable, and effective?

+5  A: 

My subjective viewpoint:

  • MEF looks like a really nice plug-in framework; it's not designed to be a full-fledged DI framework. If you don't need the live swappable components, investigate full DI/IoC Container frameworks. Unity is Microsoft's alternative.
  • Make sure you are not doing the Service Locator anti-pattern. Use constructor injection of interfaces whenever possible. See this great post by Mark Seemann and this one by Jimmy Bogard. Your statement that you "utilize the container as much as possible" is a concern - few classes need to know anything about the container.
  • Get a really good mocking/isolation framework and learn to use it well. I love Moq. Try to do state verification on the system under test rather than behavior verification on the mock whenever possible.
  • Read The Art of Unit Testing. Read other books and articles on unit testing. Practice TDD. Keep learning.
  • Read Clean Code and insure that your classes follow the SOLID principles (especially the Single Responsibility Principle). Lengthy mock setup is a code smell; your classes are probably doing too much. High code coverage is nice, but a better metric might be cyclomatic complexity.
  • Don't worry about your unit tests taking longer to write than the production code. Treat your tests like production code, though, and remove duplication whenever you can preserve readability and maintainability.
TrueWill
You bring up A LOT of good points. After reading the article on the Service Locator anti-pattern I feel strongly that this is exactly what we are doing and partially to blame for some of the issues that we are experiencing. I have created so many classes to just make the code 'feel' cleaner. I think we may have jumped the gun on MEF and may, in turn, be stuck with it. I'll have to ponder how we could rework such a large dependency on it. Moq is the mocking framework I chose and do like it a lot. I feel a large refactoring in my near future...
Adam Driscoll
@Adam - Thanks! At least you have tests to support your refactoring! :) In reading this over again I came off sounding high-handed; I apologize for that. Sounds like you're on the right track.
TrueWill
No worries. I don't think it was high-handed at all. Cheers!
Adam Driscoll
A: 

Here are some good links about DI and good design practices (in terms of writing testable code) that you might want to check (google tech talks):

I found them very helpful with plenty of good tips on testable design.

ratkok