I began writing unit tests to verify the correctness of my code nearly 15 years ago. At that point in my career, I was earning my living writing Smalltalk. That was a time when developers used to actually subscribe to magazines and these magazines were delivered monthly right to your door via the U.S. Postal Service. For a mere $129 per year, I got 12 monthly issues of The Smalltalk Report. In October of 1994, Kent Beck wrote an article published in The Smalltalk Report entitled Simple Smalltalk Testing. After reading that article a couple of years later, I downloaded a testing framework he wrote called SUnit and have since written too many unit tests to count.
Until recently, my unit tests were cobbled together with all of the various classes in the application. Though I’d been writing unit tests for nearly 15 years and arguably they were effective, they were complicated, brittle, difficult to maintain and even more difficult to comprehend. I violated practically every best practice relating to writing effective unit tests (but I’ll save this for another blog article).
About three years ago, I was first exposed to the concept of mocks as they relate to unit testing. This was a completely foreign concept to me and I must admit that I initially did not understand them or why they were necessary. After all, I have all of the classes in my application available to me, why on Earth would I ever need mocks?
Over the course of the past year, I really began focusing on honing my unit testing skills and I’ve come to love mocks and what they do for me. The quality and effectiveness of my unit tests have improved 1,000% and my use of mocks has made a world of difference for me. In talking with many of my colleagues and others I’ve met at conferences or on Twitter, it’s clear to me that many developers are confused and bewildered about mocking as I was. So, I decided to write about it and share with all of you in hopes that I might help clear things up a bit.
This is my first lengthy blog post and it will hopefully be the first of a handful of writings on the topic of unit testing and mocking.
Part 1 of this series on mocking and testing will focus on the concepts. Since this is primarily conceptual, I’ll not deep dive into code or frameworks until Part 2.
In this article, I will answer the following questions:
Outside of the world of software development, the term “mock” means to imitate or to mimic. A “mock” can therefore be thought of as a stand-in, an imposter or as most commonly referred to as it pertains to software development, a fake.
Fakes are often used as stand-ins for dependencies of the class under test.
Throughout the remainder of this post, I will use a realistic, but simple example for illustration purposes. The classes in my example are not unlike classes that may exist in your application.
LoginCommand is a very simple class that executes a login service. It so happens that the login service is a separate class and therefore a dependency of this class. Notice that the login service is expressed as an interface and that there is some mildly interesting validation logic.
ILoginService is a dependency of the LoginCommand and is a simple interface declaring only two functions; login and logout.
LoginCredentialsVO is a simple model class that wraps two pieces of data required to login; the username and password. The “VO” suffix identifies this class as a special type of model known as a Value Object.
Given this small collection of application classes, I would likely choose to unit test the LoginCommand as it contains both logical code and some interesting behavior that I would like to verify. Consequently, the LoginCommand would become my Class Under Test.
When you write unit tests, you are exclusively concerned with verifying the behavior of the Class Under Test for all reasonable scenarios. Conversely, you are not concerned with the behavior exhibited by any of the dependencies when the Class Under Test interacts with them during the execution of tests.
For example, invoking the LoginCommand.execute() function potentially invokes the ILoginService.login() function. However, we are not concerned with what happens when the service is invoked.
When we execute our tests against the LoginCommand, the only thing we can really test is various scenarios that invoke the LoginCommand.execute() function. After all, it is the only public function. Here is a list of scenarios we might like to test:
When we execute our tests, we must make sure that the LoginCommand has a reference to an ILoginService implementation. If that dependency is not provided to the LoginCommand, a Null Pointer Exception will result and all of our tests will end in error.
Our application will certainly contain an implementation of the ILoginService that invokes a remote service using mx:RemoteObject, mx:HTTPService or mx:WebService. If our application already contains such a class, why not just use that in our tests? After all, we need to test that, right?
Take a moment and think about what happens to the level of complexity in our unit tests if we use the real implementation of ILoginService that communicates with a remote service somewhere on our local machine or on the network. Suddenly, in order to run our unit tests, we have to have a running service somewhere. We also have to make sure that the machine running the tests is connected to the network. We must also ensure that the username and password are valid and that the invocation of the real service will not fail. What if the server-side code has not even been written yet? Pulling in this real implementation causes us to have to worry about all sorts of things that could cause our tests to fail. While testing with the real service might be a desirable goal, it is not a goal of a unit test.
Instead, wouldn’t it be cool if we could plugin a fake service that didn’t really do anything? Wouldn’t that be much simpler? If we do that, we no longer have to worry about networks and servers and real usernames and passwords. Using a fake greatly simplifies our unit testing.
So, how do we provide the fake to the LoginCommand? There are a number of ways you can probably think of. We could create our own fake implementation of the ILoginService class. That’s simple enough. There are only two functions we would need to implement. If we didn’t happen to express the service as an interface, we might instead choose to subclass the real service class and create a fake subclass by overriding the public functions.
Both of those alternatives involve a similar amount of effort and result in extra code to write and extra classes to create and maintain. Wouldn’t it be great if something could just generate the fake for us and we don’t have to worry about creating new classes and maintaining them? There are mocking frameworks that do just that. But, I’ll save the specifics of that for Part 2.
There are a handful of other issues that may result from using real application classes as our dependencies. When we write a unit test, it is essentially a class not unlike any other class. The class has code that creates an instance of the class under test, invokes a function on it and observes and verifies what happens.
If we use a real service, how is our test supposed to verify whether the LoginCommand invoked the ILoginService.login function? It is simply standing off to the side observing what happened. From a testing perspective, we only want to verify that the LoginCommand invoked the ILoginService.login function with specific arguments.
So, let’s summarize why we mock. First and foremost, we use fakes to simplify our tests. Secondly, there are certain types of verifications that are difficult or outright impossible without using fakes.
You might be wondering at this point, why there was no discussion of making a fake UserCredentialsVO. While you certainly could do that, what benefit would there be? This application class is sufficiently simple and its use in unit testing scenarios does not introduce additional complexity.
When it comes to mocking, there are only 3 things you really need to worry about; stubbing, setting expectations and verifying.
Some unit test scenarios don’t involve any of these, others involve only stubbing and others involve setting expectations and verifying.
Stubbing is the process of telling your fake how to behave when it is interacted with. You can generally stub public properties (those with getters and/or setters) and public functions.
When it comes to stubbing functions, you have a lot of choices typically. You may wish to return a specific value, throw an error or dispatch an event. Further, you may wish to indicate that the function behave differently depending upon how it is invoked (i.e. by matching the types or values of the parameters passed to the function).
If this sounds like a lot of work, it can be, but it generally isn’t. One great feature of many of the mocking frameworks is that you need not stub void functions. Nor do you have to stub any functions that are not invoked or properties that are not consulted during the execution of your tests.
One of the key features of a fake is the ability to tell the fake what you expect when your test runs. For example, you may expect that a specific function be invoked exactly 3 times. You may expect that it never be invoked. You may expect that it be invoked at least twice, but not more than 5 times. You may expect that it be invoked with specific types of arguments or specific values or any combination of the above. The possibilities are endless.
Setting expectations is the process of telling your fake what you expect to happen to it. Remember that since it’s a fake, nothing actually happens. But, your class under test is none the wiser. From its perspective, it invoked the function and expects that it did whatever it was supposed to do.
For what it’s worth, most mocking frameworks let you create mocks of interfaces or public classes. You are not limited to having to mock only interfaces.
Setting expectations and verification go hand in hand. Setting expectations is done prior to invoking the function(s) on the class under test. Verification is done after. So, first you set expectations, then you verify that your expectations were met.
From a unit testing perspective, if your expectations were not met, the unit test fails. For example, if you set the expectation that the ILoginService.login function should be invoked exactly once with a specific username and password, but it was never invoked during the execution of your test, then the fake would not verify and the test should fail.
There are many benefits that are to be had by using mocking instead of real application classes. These benefits include:
My goal in writing this blog entry is to introduce you to the concept of mocking in regards to unit testing. In Part 2, I will show some code samples and show you how concise and simple unit tests are when using a mocking framework. I will also introduce you to my favorite mocking framework for AS3, but include references to others that I’m aware of.
I hope that you found this article helpful and I welcome your feedback, questions, comments and input.