The name of my first test is IndexShouldThrowIfStartDateIsInvalid. I am using convention to declare what I expect the test results to be. It is very important that tests be this singularly focused. I am not testing all the parameter validation at one time, since order depence can result in subtle bugs. I’m also not simply skipping input validation, assuming that it will be handled later. I’m explicitly testing my input validation from the very beginning. Once you get into the flow of doing this kind of testing, these tests only take a few minutes to write, but their benefits continue throughout the rest of the life of the project.
So, here is the finished code for this first test:
Very, very simple! Unit tests should not be too long. If they are, then it’s probably because you’re testing too many things. So, just as the test name implies, I’m feeding my controller bad data intentionally, and then checking the results of how my controller handles that data. I expect to get an exception, so let’s look at the controller code:
Since I know the MVC framework will handle creating a DateTime object from the data passed in via the query string using a parameter of the same name, I really only need to test whether or not a date was passed. My controller logic requires a start date, and an end date, and the end date must be later than the start date. So for this first condition, I check for an empty date. If I received an empty date, I throw a special JsonException. You can see that in the catch block for JsonException, I do the following:
- Log the exception – all exceptions should always be logged
I also include a default catch block to handle any unexpected exceptions in the same way.
With this done, my test will now run and pass.
That concludes one iteration of the process. I can now check this code in! It will not break the build, it’s legitimate (though incomplete) code. Repeat this process for the remaining conditions, such as:
- Test the end date parameter
- Test that start date and end date are correct with respect to each other (i.e. end date comes after start date)
- Test that a valid logged in user initiated the request.
After all of the tests for bad data conditions are finished, the last thing to do is write a test for the valid case. With all of these steps completed, you now have a solid, reliable, repeatable way to test this controller. When it comes time to refactor this controller, you can do so with confidence, knowing that this path through the code will always be exercised automatically when the code is checked in to the central repository.