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 Ten – More Refactoring and NUnit Features
A goal of well written unit tests is to keep your test isolated. This means that even if your code under test relies on or is dependent on another class or external service you should be able to write your tests to exclude these dependencies and test only what’s in your current class or method. Sound impossible? It’s not, in fact if you’ve read my previous posts on Dependency Injection you already know half the answer to this problem. The other half of the solution is mocking.
Most software you develop is made up of a variety of classes and components. Ideally, each of these classes and components are designed to perform a specific and specialized series of tasks or meet specific requirements, per the Single Responsibility Principle (SRP). These classes and components are composed together to create an application. The specialized nature of the individual classes and components necessitates dependencies. Your user interface is dependent on your business domain classes, and those same business domain classes are themselves be dependent on things like external data stores (databases, file systems), web services or other external resources and systems.
When we write unit tests, we need to remember that these test should be focused on the specific code that we want to test. Consider the following code listing:
The complete code for this example is available at the link above.
This represents a pretty common type of method in the business layer of a Line of Business (LOB) application. You’ve probably written methods like this dozens, if not hundreds of times. In this case the metheod is placing an order in an e-commerce type of application. I have a method called PlaceOrder (line 14) that contains some business domain logic to create an order for a pre-defined customer. Like many LOB applications I have a need to persist some information, which in most cases means saving some data to a database. My class has a method called Save (line 28) that makes a call to an instance of an object that implements the IOrderDataService interface that is an abstraction of my database. I also need to deal with a Customer service to link orders to customers (line 22). In this case there is also a requirement to log all orders, so I need to make a call to a logging service (line 23).
This one method, which when developed using TDD would likely have several unit tests covering it, has dependencies on three different external resources. But as a developer writing a unit test I don’t want to execute the code in the external resources, just the code in method and class under test. This would mean the code in the PlaceOrder public method as well as the code in the private Save method which is called from PlaceOrder and in the OrderService class.
There are many reasons I don’t want to use the actual run-time class and components for my dependencies. As mentioned, I want my test to be focused on the specific code (method(s) and class) under test. This way if (when) my test fails I have a much smaller area of code that my defect could be occurring in. I don’t have three or four layers of architecture to dig through, just a few methods and (hopefully) a dozen or so lines of code. This makes finding a locating the defect much easier and faster.
Another reason I don’t want to use the actual classes for these tests is that they can make the tests unpredictable. If my unit test has to read a database each time it’s run and expects a specific value to be present in the database I’m at the mercy of that database. What happens if another developer is expecting a different value in that database? What happens if another developer is running a test that changes that value? Suddenly my test becomes unpredictable, passing one moment and then suddenly failing the next despite the fact that the code has not changed. I not only want to keep my test isolated to the code under test to help track down where the defect is, I want to isolate it to keep the tests consistent.
Speed is another issue. I want my tests to be fast. Code that has to interact with an external resource like a database or a web service will run slower due to the latency in the communication with these external resources. This latency may not seem like much for one test that’s making one call to my database or web service. But when I have hundreds of tests this time adds up quickly. The mocks of these external resources respond much faster than the actual external resources, meaning that my entire suite of unit test can run in seconds, not minutes.
This isn’t to say that I never want to write tests that use my external resources. In addition to TDD and unit tests, integration tests (tests that verify the various parts of my application work together) are a very important part of software development. In TDD we don’t write integration tests, but they should still be written, and I will cover these types of tests in a future post.
As I mentioned in the introduction, DI is the first step in my solution to this problem. As you can see in the above example (line 7) I have a constructor for this class that takes my dependencies as parameters. You can also see that these dependencies are based on Interfaces. As you’ll recall form a previous post in this series, this means that I can not only pass in the normal run-time objects to this class, I can pass in ANYTHING that implements the required interfaces. This is the crux of mocking; I can pass in mocked or fake objects that can stand-in for the actual run-time objects that my OrderService would normally use. These mocks can return canned responses, count the number of times they were called and even enforce basic validation rules; everything except that actual functions the normal object would do. That means they don’t actually implement any business algorithms or interact with any external resources. We can run our tests as much as we want and we don’t have to worry about the state our database is in or about changing that state.
“Mock” has become a generic term for the various types of mocks that you can use in your unit tests. So when is a mock not a mock? When it’s a stub. Or a fake. What’s the difference? To paraphrase the very smart Martin Fowler:
Looking at the list above, there are some types of mocks that we could certainly write ourselves, like Fakes and Dummies. We could also write a simple Stub, but at this point the amount of time I’m spending creating my mocking structures starts to have an impact on my productivity. Luckily there is a better way; use a mocking framework. There are many mocking frameworks available for TDD in .NET. For this series I’ll be using JustMock and JustMock Lite. (I’ll designate which I’m using in each post/example).
Mocking frameworks are really must-have tools for TDD. Just as a unit test framework enables us to run our tests in a test runner without having to write our own test harness, mocking frameworks make it easy for us to quickly and easily create all the types of mock objects listed above. Mocking frameworks usually include some sort of interface to create simple stubs that can return values or throw exceptions based on a pre-defined set of input parameters, create more highly featured mock objects that track a set of execution rules and even create spies by substituting a set of logic we supply for a method on a concrete object. To attempt any of these manually is possible, but as with attempting to write your own unit test framework from scratch or design and build your own DI container framework, the return on your investment in time and effort is very limited.
In software development, binding happens. But we still want to isolate our code under test. In concert with DI, Mocking enables us to have this isolation by creating stand-ins for the classes and components our class and code under test are dependent on. There are many types of mocks and each serves a specific purpose. A good mocking framework will help you write the right type of mock you need for your test.
In the next post I will show you how Stubs can help us write some tests for the code in this post.
Continue the TDD journey:
Subscribe to be the first to get our expert-written articles and tutorials for developers!
All fields are required