Archive for the ‘mocking’ tag
TDD: Mock expectations in Setup
One of the ideas that I mentioned in a recent post about what I consider to be a good unit test was the ideas that we shouldn’t necessarily consider the DRY (Don’t Repeat Yourself) principle to be our number one driver.
I consider putting mock expectations in the setup methods of our tests to be one of those occasions where we shouldn’t obey this principle and I thought this would be fairly unanimously agreed upon but putting the question to the Twittersphere led to mixed opinions.
The case for expectations in setup
The argument for putting expectations in the setup method is that it helps remove duplication and helps us to fail more quickly.
This would certainly be the case if, for example, we instantiated our object under test in the setup method and there were some expectations on its dependencies on creation.
The case against expectations in setup
The reason I’m so against putting expectations in setup methods derives from the pain of trying to debug NMock error messages when we put expectations and stubs in the setup method on a project I worked on about a year ago.
The number of times we were caught out by a failure which seemed ‘impossible’ from looking at the failing test was ridiculous.
After that experience we made sure that it was always obvious which expectations belonged to which test by inlining them and taking the duplication hit.
I believe a lot of the value of tests comes from the way that they fail, and if we can write tests in a way that the failure message and subsequent fix are really obvious then we are going the right way.
My current approach
My current approach to try and get the best of both worlds is to follow the approach Phil describes in his post on Domain Driven Tests.
If we have repeated expectations across different tests then I now try to extract those into an appropriately named methods which can be called from each test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | [Test] public void ShouldDoSomething() { ExpectServiceToReturnSomeValue(); // rest // of // test } private void ExpectServiceToReturnSomeValue() { // code describing expectations } |
This creates a little bit of duplication in that we have to call this method individually in each test which uses it but I think it makes the test more readable and easier to debug.
I’m still not sure what I consider the best way to name these types of methods – Phil uses a combination of a comment and method name to create readable tests but I’m keen to try and have the intent completely described by a method name if possible.
TDD without the design
Roy Osherove and several others have posted recently about introducing TDD to the ‘masses’
As I understand it Roy’s idea is to separate the learning of TDD from the learning of good design principles – good design principles in this case being the OOP principles described in Uncle Bob’s Agile Software Development Principles, Practices and Practices or on the Object Mentor website.
I am usually in favour of an approach that breaks a skill down into chunks so that it is easier to learn but in this case I feel that some of the big gains in coding in a TDD way is the decoupled design it encourages, which in my experience is more likely to follow good design principles.
One of Roy’s suggestions is that popular mocking frameworks such as Rhino Mocks, Moq and NMock make it harder for people to adopt the TDD approach and that other mocking tools such as JMockit or Typemock Isolator would be more suitable.
When I first learnt how to TDD I didn’t know about any mocking frameworks and while I could see some value in the approach, I was often writing ‘unit tests’ which connected to real resources therefore losing the quick feedback cycle which TDD encourages.
Ian Cooper believes that the most important step is starting to use TDD rather than doing it correctly straight away:
The difficulty with the software community is that experienced practitioners can overwhelm newbies by flooding them with information on ‘how to do it right’.
The problem is that the most important step is not doing it right, but doing it at all.
This was the route that I inadvertently went and while it does provide an incremental approach to learning I feel that I would have seen the actual value in the TDD approach much earlier if I’d learnt TDD as a design tool, therefore meaning that I would have had to learn mocking at the same time.
While it may indeed make TDD more accessible, wouldn’t it be better to have less people adopting it but that those who do adopt it really understand it and can help spread the message.
Instead of trying to reduce the learning curve, let’s try and help people to conquer it more quickly.
I appreciate that not everyone who learns TDD has the environment or opportunity to execute it perfectly but at least by having all the information available to them they can make an informed choice on how they use it rather than the scenario which I could envisage happening where you eventually end up with two versions of the same concept – Test Driven Development & Design and plain old Test Driven Development.