Unit tests follow a simple pattern. First, you create a controlled environment for the function or class you want to test. Afterwards, you call a function or method of an object or create a new instance. Finally, you assert that the function returns the expected value or the state of your object has changed as desired. Sometimes, though, tests seem to grow unnaturally large because it is ridiculously difficult to control the environment.
Quite often the issue is that the code you would like to test is too specialized to be easily testable. In this article we will learn about how interfaces and mock objects can be used to enhance the testability of your code and make it easier to reuse.
Taking the time
Think of yourself as a successful game designer. For the latest yearly update of your critically acclaimed shooter, your fans are craving for a long due feature: They want to know for how long they have been playing your game. Obviously, time spent in the main menu or the pause screen should not count as play time. To implement this feature you create a simple class:
class play_time { public: play_time(); void start_session(); void stop_session(); boost::posix_time::time_duration played_time() const; private: boost::posix_time::ptime session_start; boost::posix_time::time_duration total_time; };
play_time queries the operating system for the current time when start_session() and stop_session() are called. To this end, it uses boost::posix_time::second_clock::local_time() to stay portable across different platforms:
void play_time::start_session() { session_start = boost::posix_time::second_clock::local_time(); } void play_time::stop_session() { auto const current_time = boost::posix_time::second_clock::local_time(); auto const session_duration = current_time - session_start; total_time += session_duration; } boost::posix_time::time_duration play_time::played_time() const { return total_time; }
Issues with testing
This code seems simple enough, but it feels awkward to test. While still possible, your test requires the following steps:
- Create a new play_time instance.
- Call start_session().
- Sleep for a reasonable amount of time.
- Call stop_session().
- Call played_time() and compare it to the time slept.
The test sequence is small, but the problem lies in what is a resonable amount of time to sleep. Typical game sessions take a couple of minutes or hours, and it is reasonable to check such scenarios. On the other hand, it is impractical to sleep for the same amount of time. Imagine working test-driven and having to wait one hour for your test to execute. Even waiting for one second is too much.
Making the dependency explicit
The issue is not with the test, but with the code itself. It implicitly depends on system functionality, i.e., the system clock. The solution is to generalize this dependency and make it explicit. As long as play_time gets the current time from somewhere, it should not care about the exact source. To this end, we introduce a new second_clock interface class:
class second_clock { public: boost::posix_time::ptime operator()() const; // forbid copies of second_clocks second_clock(second_clock const &) = delete; second_clock & operator=(second_clock const &) = delete; virtual ~second_clock(); protected: second_clock() = default; private: // called by operator(). child classes must implement this function virtual boost::posix_time::ptime get_current_time() const = 0; };
The implementation of operator() is straightforward:
boost::posix_time::ptime second_clock::operator()() const { return get_current_time(); }
Now we need to use this interface in our play_time class:
class play_time { public: // use the given clock to get the current time play_time(std::shared_ptr<second_clock> clock); // previously defined methods... private: std::shared_ptr<second_clock> clock; // previously defined members... };
The constructor takes a reference (well, a std::shared_ptr to be sure that the referenced object is kept alive) to a second_clock and stores it in the clock member. We need to change the implementation of the start_session() and stop_session() methods:
void play_time::start_session() { session_start = (*clock)(); } void play_time::stop_session() { auto const current_time = (*clock)(); auto const session_duration = current_time - session_start; total_time += session_duration; }
Brilliant. All methods which have to take the time use the clock member’s operator() to do so.
Creating a system clock
In our application, we want play_time to use the actual system clock. Since play_time uses the second_clock interface to get the current time, we need a class which represents the system clock and implements the second_clock interface:
class system_clock : public second_clock { public: system_clock() = default; virtual ~system_clock(); private: boost::posix_time::ptime get_current_time() const override final; };
We need to implement get_current_time() to be able to instantiate system_clock objects:
boost::posix_time::ptime system_clock::get_current_time() const { return boost::posix_time::second_clock::local_time(); }
To express that play_time should use the system clock to take the current time, we create an object like this:
play_time time(std::make_shared<system_clock>());
It seems a little more complicated to create a play_time object like this, in particular when held right next to the default constructor of our first design. We have sacrificed a little convenience for clarity, since every user knows that our object depends on the system clock.
Create a mock class to pick the testing fruit
The real advantage we have gained is that we can pass an object of any class which implements the second_clock interface to the constructor of play_time. Even though there is a limited number of implementations which make sense for production, we are free to create a mock class we have perfect control of:
class mock_clock : public second_clock { public: mock_clock(boost::posix_time::ptime current_time); void set(boost::posix_time::ptime new_current_time); virtual ~mock_clock(); private: boost::posix_time::ptime current_time; boost::posix_time::ptime get_current_time() const override final; };
mock_clock can be thought of as an analogue clock with no batteries in it. If you do not set a new time, it will keep on displaying the same time indefinitely. A cursory observer will take the displayed time for the current time. For completeness, here is the implementation of the relevant functions:
mock_clock::mock_clock(boost::posix_time::ptime current_time) : current_time(current_time) { } void mock_clock::set(boost::posix_time::ptime new_current_time) { current_time = new_current_time; } boost::posix_time::ptime mock_clock::get_current_time() const { return current_time; }
The code is really simple, but it makes testing most effective. To test the play_time class, we no longer require sleeps, but simply pass a mock clock to the constructor and reset the time:
boost::posix_time::ptime const start_time({2013, 2, 10}, {15, 32, 11}); boost::posix_time::time_duration expected_duration(1, 2, 3); auto clock = std::make_shared<mock_clock>(start_time); play_time time(clock); time.start_session(); clock->set(start_time + expected_duration); time.stop_session(); // assert time.played_time() == expected_duration
Voilà! We have clear, expressive test code which runs fast as hell.
Common implicit dependencies
The system time is not the only implicit dependency there is. Here are a few other things you might depend on:
- Some functions process data stored in files and take the file name as an input parameter. It is a better idea to make the function work on a std::istream or std::ostream reference. Your function is more cohesive because it does not have to do two things (doing stuff and opening files). Thus, it is more reuseable. Furthermore, the standard also provides a perfectly controllable mock object in the form of std::stringstream.
- Network communication typically relies on system calls. If you create a network interface class, you can create a mock network class with which it is easy to test error cases such as packet loss or connection timeouts.
- Database code typically accesses low-level functions of a database driver. Create an interface for database access and provide a mock implementation of a database driver. It could be able to return easily controllable metadata, prepared result sets, or return error codes as the real API would do. The only difference is that you do not even need a database.
There are countless other applications of interfaces and mock objects out there. Whenever you design a class, think of which system calls or other classes this class depends. Consider making some of these dependencies explicit by defining or using appropriate interfaces. With a little experience, it is easily possible to do this test-driven as well.
Once you have an interface and a corresponding mock object, you may very well find that you can use both at more places than you initially thought. Your code becomes more general, more expressive, and easier to test. What is not to like?
Really a nice example! But would you agree that one should also try to avoid as much as possible to introduce such mockjects? Most often I think it is possible to get rid of dependencies to the (evil) environment by redesigning the interfaces without trying to mockify the environment. Maybe you can share your opinion about how much mock is still ok or wher when should even consider integration tests instead of mockified unit test.
Hi Sebastian!
You are right in that it is often possible to avoid coupling to the environment by designing your interfaces appropriately. Cohesive functions never hurt, and if it is possible to write a function which operates without the environment then go ahead.
Interfaces and mock objects are not a way of cheating which is best avoided. They are invaluable tools which reduce the coupling between different units, even in situation where some of the units are provided by the language itself. Mock objects help to test code which depends on an interface without the depending code knowing it is subject to a test. The result are clean, focused, and surprisingly lightweight tests close to the source where the dependency is used.
Integration tests, on the other hand, often feel bulky and require frameworks which cannot directly test the code in question. In general, it is also difficult to isolate the unit in question. Achieving an adequate coverage with integration tests is hard and often impossible. Nevertheless, they are very useful to establish that your application uses real, i.e., non-mock objects to fuel the interfaces.
All in all, mock objects are essential for good unit tests. The ability to introduce mock objects at will is a telltale sign for good class design and the best way to control pesky environments. Integration tests are still useful to test the real thing, but those tests can be comparatively simple since the unit tests with mock objects should already cover the entire code.
I know it’s a simple example — although by no means “toy”, it seems realistic enough. And I still don’t get it. (Pro’lly because I didn’t swallow the TDD cool aid.)
You have a conceptually rather simple class — play_time (a) — and to create a “clean” test, you replace it with:
+ A slightly more complicated class. play_time (b)
+ An additional interface (c)
+ Two concrete classes on top of the interface — system_clock (d), mock_clock (e)
Then you go ahead and write a unit test to test (b) with the help of (e).
Seriously. What have you gained? You have *more* code. You have written code your production environment never needs. (because, really, at this point in time, you only use system_clock). And, and I think this seems rather important, you are left with two concrete classes, namely system_clock and mock_clock, where you *do not* have a unit test for. Of course, testing mock_clock is easy enough, but making sure that system_clock does what it’s supposed to to, rather seems to beget a Catch22, or doesn’t it?
Honestly. I think your example is excellent, because it doesn’t show toy code, but something approaching reality. But if I were to sit in a meeting with my colleagues, I’m sure we’d come to the conclusion that the amount of work put into this vs. the payoff wrt. testing the system_time class seems ridiculous. And also see my mentioned Catch22 above.
Maybe you can take the time to convince me 🙂 cheers!
Hi Martin!
Considering TDD: I was a non-believer myself and I actively fought against it when I was first confronted with it. When I tried it myself though, the benefits were quite apparent. Once you have realized yourself that TDD makes you more productive, you do everything to make your code easier to test.
Secondly, clean code is not about writing as little code as possible. It is about writing code with single responsibility, about keeping the level of abstraction within a function constant. It is about making implicit assumptions explicit, it is about not repeating yourself all the time. Writing clean code means to make things modular and easily changeable.
In most situations, this means that you need to write more code. You introduce functions to abstract from bit-fiddling. You create classes to collect operations on related variables. You introduce interfaces to decouple your code and make choosing between different strategies easy. All this requires new signatures, names, declarations, and whatnot. You get more code. The good thing is that if you do it correctly, each bit of this code is easy to understand and to exchange. With clean code, you have lots of different units which are easily testable, and this will safe you lots of time. I recommend to read Robert C. Martin’s Clean Code book for more details or wait two years until you can read all of it here :-).
So, yes, there is more code. I do not agree that
play_time
has become more complicated. The interface looks a little more complicated, but the implementation has become easier, since the system call has been abstracted. I agree that you need to unit test bothmock_clock
andsystem_clock
. I omitted these tests from the article to keep the length reasonable. Here would be a potential test forsystem_clock
:It basically checks that
system_time
uses a system call whenoperator()
is called. In the test code, you could change the calls tolocal_time()
to other system calls if you trust those more.I agree that writing the interface, the concrete classes, and the corresponding unit tests takes time. I hope my article has convinced you that the actual code is easier to test if you have the possibility to use a mock object. Hopefully this comment has convinced you that it is possible to write unit tests for the concrete classes (we already were on the same page for
mock_clock
).For me, code which is not properly unit tested is a danger. Even though it might work now, there is no guarantee that it will work after the next change. I have personally witnessed situations in which my team was not sure if a feature ever worked or if it did, how it could have. Time does that to you. People tend to forget, and without automated tests there is no prove. Tests save time, even if it means a little more extra work beforehand. If the additional effort is worth if you only need the time at one place is up to you to decide. However, you should keep in mind that you could need a clock in other situations. Every time you need a clock, you need to test it somehow. With a little effort up front, subsequent tests are really easy and cheap.
Please let me know if this cleared things up a little 🙂
Michael
Most appreciated!
One detail you showed now — and one I didn’t see clearly before — is that testing play_time(2) + system_clock + mock_clock is actually *easier* (albeit more code) than testing the play_time(1) version, where you outlined “requires the following steps” in the article.
cheers!
Thank you so much for saying this. I have been trying to convince colleagues for years now of this fact. The test of a clean and well designed class or piece of code is looking at how many responsibilities it takes on and how coupled it is to the rest of the system, and not how many lines of code it is. I always get so frustrated when someone comes back in a code review to tell me that the diff for my change is too big, when it is in fact one very small piece of functionality, encapsulated by an interface and covered by a large test suite.
Why not use a mocking framework to do that mocking-class creation for you? It looks like you’re reinventing wheels that have existed – even for C++ – for 5 years now.
No mention of mock frameworks like Google’s mock? gMock is AMAZING.
To Martin: I once adhered to the idea that you should not litter “testing residue” in prod code. That lasted right up until I had to write REAL production code in C++ and hit something like this case, where a unit test took minutes to run. I still hold that idea as much as possible, but now I deeply embrace dependency injection and mocking as the very very best way to write well factored and test-isolated code.
It does leave a bit of residue on prod code, but the benefit FAR outweighs the cost.
Hi Peter, Hi Tim!
Thanks a lot for putting mock frameworks into play. I believe that before using a mock framework you should understand how mocking works and why it is useful. That is the primary purpose of this article. Perhaps in this light you may excuse me writing mocking code manually and explaining how it works.
The application of mock frameworks is an interesting topic I must admit I am not as familiar with as I should probably be. I will take your hint as motivation to do some research. Be sure to read about it in the near future!
Cheers
Michael
I was pretty skeptical on TDD until I read Growing Object Oriented Software: Guided By Tests. I’m not 100% sold on TDD but I’m pretty sold on mock testing.
More than a a third of that is a worked example of the judicious use of a good mocking framework to create system and integration tests to test and guide the design as it happens. A few people skim the code in that book, I’d highly recommend against doing that.
I read, recommend and loan a lot of technical books, and this is one of my top 5.
I second the recommendation of using a mocking framework and a unit-test framework, although I understand your desire to keep things simple in an introduction. I also recommend you take a look at googlemock aka gmock. I introduced googlemock in my last project (although we used UnitTest++ for the test framework). It comes with its own test framework, gtest, which is quite useful, although I lean toward using Boost.Test, if I have a choice.
Also there is book in beta from the Pragmatic Programmer folks called “Modern C++ Programming with Test-driven Development” you might be interested in. It uses gmock/gtest as it’s primary tools.
Maybe after a long gestation period, we could have a standard unit-test/mocking framework added to the C++ standard so that this wheel wouldn’t have to be re-invented.
P.S. Keep up the good work. I stumbled upon your web-site and have recommended it on Google+ and Facebook.
Hi Michael!
Thanks for the feedback! I am glad to hear you like our site and recommended it! Since I wrote this article I have started toying with gmock myself. It really is quite useful and many tests become much cleaner and simpler with it. I hope I will be able to write the follow-up article in the near future.
Thanks for the book recommendation. I enjoyed previous entries in the pragmatic series I will definitely have a look into it.
And yes, I would love to see unit-test/mock support in the C++ standard. It sure would make things a lot easier.
I realy like this post. However I would not call it Mock objects. I know there is a lot of confusion in the community and different authors use different wordings, thats why I recommend to stick to the terminology used by Meszaros (http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html). What you showed in your post is actually better called a Stub,because your “Mock Object” does not care (and check) if it was called correctly or called at all. Mock Objects usually do care.
Apart from this thanks for sharing your knowledge.
Agreed, this article is talking more about “fakes” as opposed to “mocks”. See Martin Fowler’s definitions here: https://www.martinfowler.com/articles/mocksArentStubs.html.