If you’ve never done Test Driven Development or aren’t even sure what this "crazy TDD stuff” is all about than this is the series for you. Over the next 30 days this series of posts take you from “I can spell TDD” to being able to consider yourself a “functional” TDD developer. Of course TDD is a very deep topic and truly mastering it will take quite a bit of time, but the rewards are well worth it. Along the way I’ll be showing you how tools like JustCode and JustMock can help you in your practice of TDD.
Previous Posts in this Series: Day 14 - “Simple” Does Not Always Mean “Obvious”" Pt. 1
In the last post we wrote what should have been a simple test. But in this case we’ll see how sometimes simple tests can be a little more complicated that we anticipate.
In the last post, we received this test case:
When a user places an order for an item and the quantity ordered is zero, then an InvalidOrderException should be thrown.
From this we ended up creating this test:
(get sample code)
The test passed, but there is still a problem. We want to make sure that the PlaceOrder method on the OrderService class (our code under test) throws an InvalidOrderException and does not call the Save method on the OrderDataService class. The test seems to validate that; we have told the test to expect the InvalidOrderException to be thrown and have setup our stub to expect the Save method to never be called. The test passes, so what’s the problem?
The problem is that the test is marked as having passed, but the Mock.Assert call on line 19 is never called. Don’t believe me? Let’s try an experiment. Let’s change the OccursNever call on line 13 to be OccursOnce:
If our test is executing as we expect then the test should fail.
Figure 1 – Uh oh.
Since the test didn’t fail when we changed one our exceptions to the opposite of what it should be we have a problem. Clearly our Mock.Assert is not being called.
Turns out the problem is NUnit. Or, more specifically, the ExceptedException attribute of NUnit. What this attribute essentially does is tells the test runner to wrap your whole test method in a try/catch block and catch a specific exception type. The call to PlaceOrder on line 16 results in an exception being thrown. Since we are not catching that exception in our test it bubbles up to the test runner. Once the test runner catches the exception that it’s looking for it passes the test and does not execute any of the code following the line in the test the exception what thrown from.
In many cases using the ExpectedException attribute is fine. We have now encountered a situation where it is not, so we need to find another, more manual way. The first thing I need to do is remove the ExpectedException attribute from my test (line 2 in previous listing). I then need to wrap my call to PlaceOrder on line 17 in a try/catch block:
(get sample code)
To verify my code I want to specifically catch an InvalidOrderExpection exception being thrown from the PlaceOrder method. In this test I setup my catch to catch that specific type of exception (all other exceptions get bubbled up to the test runner and fail the test). After catching this exception I want to assert that my stub was properly used, so I call Mock.Assert. If that works I need to tell my test runner that the test has passed, so I call Assert.Pass which tells the test runner the method is over and the test passed. If another type of exception is thrown or the method doesn’t throw an error the code falls through to the Assert.Fail call on line 27 and the test is failed.
We’ll go ahead and run this test, and see that if fails (Figure 2):
Figure 2 – Our test still fails
This fails because the setup for our stub still expects Save to be called exactly once. I left this here so we could see that with the newly refactored test we are in fact asserting that our stubs are being used properly. Changing the setup for the mock back to expect the Save method to never be called puts our test back the way we actually want it (line 12):
We run the test and we can see that it is now passing (Figure 3):
Figure 3 – Passing test
NUnit provides a lot of things to help us write better, easier to understand tests. But it’s important to understand how these things work and when they are not appropriate to use. As this series progresses I’ll point out more idiosyncrasies with the NUnit framework and how to negotiate them.
Continue the TDD journey:
Subscribe to be the first to get our expert-written articles and tutorials for developers!