By this point in the series, you should know quite a bit about the basics of unit testing in .NET. You should be able to write unit tests using NUnit, and you (hopefully) understand the value that unit testing brings to the table. As you’ve started to apply what you’ve learned though (and you are applying what you’ve learned, riiiiight?), you might have run into things that were difficult to create test cases for. Worse yet, maybe you ran into things that you couldn’t test at all. Fret not, as post 4 in this series is here to save the day. In this post, we’ll look at some of the basic testing problems that you are likely to encounter and how to overcome them. Let’s get started:
Classes with complicated setup
First, some classes require complicated setup or configuration before they’re testable. If you have multiple test cases for your class, you could end up with that setup code repeated in a lot of places. Having to rewrite all that code is painful, and it will probably make you cut corners to keep your number of test cases low. Even if it doesn’t, what happens if the setup or configuration steps for the class change? Now you have to change that code in a lot of places.
The solution is simple: Don’t Repeat Yourself (aka the DRY principle). DON’T repeat the configuration or setup code. Encapsulate it instead. Some people choose to make a field in the test fixture for the class-under-test (the class we’re creating unit tests for), then use a test case setup method to populate the instance for each test case like so:
As a matter of personal preference, I tend to avoid this approach. What happens when you have a test case that needs to configure the class-under-test a little differently than usual? You could just reset ignore the private field and make a local instance, but that private field has still been instantiated. Is it hurting anything? No, probably not, but it’s still there, and that bugs me. Plus, I don’t like test fixtures having state, which is exactly what a private field is.
Instead, I encapsulate the common setup logic in a private helper method that my tests can call to grab a fresh reference to the class-under-test, like so:
If the test case just needs an instance with the default configuration, it can call the helper method, otherwise, it can perform it’s own setup and configuration. Yes, you are technically repeating yourself since many of the test cases will have a line of code to retrieve the class-under-test, but DRY is more of a guideline than an actual rule. 😛
Addendum: One time when I will go the test setup route is when the class-under-test also requires some special cleanup. While this should be avoided if at all possible, sometimes it’s necessary. In those cases, I will use a test setup method combined with a test tear-down method.
In a perfect world, all your classes would be fully self-contained and would have no dependencies on other classes, making it very easy to create unit tests that only stress the class-under-test. In the real world though, this almost never happens. Depending on what the class-under-test is dependent on, this can be a real problem. Suppose your class makes calls into some other class, and suppose those calls are expensive (on the order of minutes). Suppose that you already have thorough unit tests for that other class. Well, your test cases for the class-under-test are going to take a long time to run, and they’re going to exercise already tested code. What can you do? Let’s take a look at the original class:
My recommendation is to use interfaces everywhere, and apply Dependency Injection. When you apply Dependency Injection, the instantiator of your class becomes responsible for specifying which concrete dependencies to use. When you combine this with interfaces, it becomes quite easy to mock access to external classes. Here’s our code, but modified so that we can apply Dependency Injection:
To test the class without exercising LongRunningService, we create a private class that implements the IService interface, and pass that in through our unit tests, like so:
Notice how I’ve given DependentClass a default constructor that takes care of instantiating the "real" concrete implementation. While this approach works, a more robust solution is to use an Inversion of Control Container, which we might explore in a future post.
A related testing problem is error-handling logic. Error-handling logic for method parameters is quite easy to test (just pass in a bogus parameter from your test), but simulating error handling around an external resource, such as a web service, is much more difficult. Let’s take a look at a method from our old friend, the Account class:
How can we force our web service to fail so that we can verify our error-handling logic? Well, we could have our test disable the network card in the machine or something like that, but there’s an easier way. Again, by using interfaces and dependency injection, we can easily simulate whatever error we desire.
With the refactoring in place, all we have to do is create a test class that implements that interface, then pass that test object in to our class-under-test, like so:
Dependency injection and interface-driven design can make testing simpler and save you a lot of headaches, but there are times when applying it may not seem so obvious. Let’s look at an example class that takes in log messages and sends an E-mail if the log looks like an error:
To fully test this class, we need to verify that an E-mail is sent when an error message is passed in. SmtpClient is a sealed class that we don’t have access to, and it doesn’t have an interface, so we can’t directly apply dependency injection. Fortunately, there is a simple pattern we can apply to solve our problem: the Adapter Pattern. First, we create an interface that abstracts the parts of SmtpClient that our application uses, like so:
Next, we create an adapter class that wraps SmtpClient into our interface:
Finally, we apply dependency injection to our class-under-test, and update our test cases, passing in a mock that we can use to verify that a mail message was "sent".
Database Access Code
The mother of all external dependencies is database access code. How do you test code that must interact with a database? In some cases, we can apply what we’ve learned (dependency injection, the adapter pattern, etc), but those solutions won’t scale well to a complicated system with a lot of data access logic, and if you are trying to mock classes from the System.Data.Sql namespace, You Are Doing It Wrong. You can’t just let your classes-under-test access the real database, either, because that’s a shared resource; changes made in one test will be visible in another, a clear violation of the principles of unit testing. Using a real database heavily in your unit tests will also guarantee that your tests will take forever to run. Remember that unit tests should execute quickly (and consistently), so that approach is not viable.
Unfortunately, I have yet to find a good solution to this problem. I have taken two approaches in the past, both of which have some downsides. One approach was to wrap all data access code in an interface. We then created two classes that implemented that interface: one was real (it actually talked to SQL Server), and the other was a horrible mess of code that was basically an in-memory database. Aside from having to maintain a complex code base to facilitate testing (our in-memory version is about as many lines of code as our real SQL version), developing and testing against the in-memory version led to a lot of problems. The in-memory version didn’t always enforce the same constraints that the real version would, so we would frequently run into things that worked fine in testing but failed in production.
If you are using an ORM, your options are slightly better. With Castle ActiveRecord or NHibernate, you can use SQLite, and you get mostly equivalent functionality to a real SQL Server database. You also have less code to test since most of the common CRUD stuff has been done for you. This approach can be problematic to setup and maintain, but it’s the best solution I’ve found so far. You can find out more about how to set that up with ActiveRecord in my post on Easy testing via ActiveRecord and SQLite.
Finally, one of the most commonly asked unit testing question is "how do I unit test my GUI?" My answer is "you don’t." Your code should be structured so that the code-behind for your GUI contains a *minimal* amount of logic. Regardless of whether we’re talking WinForms or WebForms, your GUI should contain only the logic dealing specifically with presentation (layout and formatting), and nothing more. Anything beyond that should go into "middle tier" classes, classes that can easily be tested. Here’s an example for a BAD event handler:
And here’s what the event handler looks like once you’ve refactored it:
If you absolutely HAVE to write test cases for a GUI, there are toolkits that can help. I have very little/no experience with these, so use at your own risk (and remember, good design is a almost always a better answer):
If you have experience with any of these tools, please chime in below and let myself and others know what you think of them.
In today’s post, I’ve gone through some of the commonly-encountered unit testing problems, and I’ve hopefully shown you how to address them. In the next post, we’ll look at how to take make it easier to apply dependency injection and the adapter pattern through the use of the excellent Moq mocking framework. Look for that post on Friday at the earliest.