« Archives in January, 2021

This Is Why We Mock, Part III

The refactorings I made in the previous episode, laid the groundwork for unit-testing. Now has come the time to talk about mocks. What are mocks, anyway?

From a component developer’s point of view, there are usually “foreign” components as well; that is, components that you are dependent on but you don’t want to unit-test. You don’t even want to #include or link-in such components when executing your unit tests. Often, such foreign components are developed by another person, department, or organization. In other cases, these components run only on a particular hardware. Sometimes, these components are not (fully) implemented yet when you start developing your component. Foreign components are outside the scope of the UUT (unit under test).

Mocks act as substitutes for foreign components. They adhere to the same interface as the foreign components, otherwise they wouldn’t be compatible. The cool thing about mocks is, that you, as a unit test developer, can specify how the mock should behave. So when you have a mocked Person class with a method “greet”, and the real implementation would normally respond with “Hello”, you could instruct your Person mock to return “hello”, “howdy”, “”, a nullptr, basically anything you want. This allows you to write unit tests that check that your UUT code behaves correctly, no matter what is returned by the greet method. Setting-up such behavior in the real foreign component would be extremely difficult, if not impossible. A mock is basically a puppet on a string — you pull the strings and it behaves accordingly.

Let’s do a concrete example using our temperature control app. We defined an “ISensor” interface and rewrote the whole app to only work against this interface. From this interface, we’ll derive a mock (I’ll use googlemock, one of the most popular, powerful, and reliable mocking frameworks for C++).

I suppose you can easily figure out what MOCK_METHOD does: It declares a method “getTemperature” that returns an “int” and takes “status” as a parameter, which is exactly the signature defined in “ISensor”. Now let’s toy around with this mock a little bit:

The interesting part is what happens in EXPECT_CALL; it basically means this:

  1. We expect, that during the execution of our test case, there will be exactly one call to “getTemperature” (“WillOnce”).
  2. We demand that the call to “getTemperature” yields a value of 200.

When we call the “getTemperature” method on the next line, “sensor” (our mock) will comply with our demand and return 200. That’s why the EXPECT_EQ statement will succeed. Let’s call “getTemperature” twice:

In this case, we’ll get

If we wanted to set our expectations such that “getTemperature” is called multiple times, we could use “.WillRepeatedly” instead of “.WillOnce” and our test would thereafter pass.

What about the “status” parameter? Is there a way to demand that output parameters are set to a certain value as well? You bet!

Here we have multiple demands on our mock (“DoAll”). The mock will set the value of the dereferenced first pointer argument to “bad” and the return value to 200. The same goes (with different values) for the second test case. Nice, isn’t it?

Since we now have a basic understanding of how mocking works, we can resume our task of unit-testing our temperature control app. Instead of using concrete sensor and heater objects, we’ll dependency-inject their mocked counterparts and set expectations and demands on our mocks. We then execute our UUT (the controller) and check if it behaves correctly (expect that the heater mock is turned on or off). For brevity’s sake, I’ll only show the “happy path” where the sensor status is never “bad”:

The remaining test cases can be found in this GitHub repository.

This concludes my mini series on mocking in C++. My aim was not to teach you all about mocking (or googlemock in particular), but rather wet your appetite. Another aim was to raise your level of awareness regarding “design for testability”, especially programming against interfaces instead of concrete classes. Your foreign components should always be derived from pure interfaces. Otherwise, you simply can’t dependency-inject mocks during unit testing — at least not in C++.

To be able to fully unit test our code — this is why we mock!

Previous episode

This Is Why We Mock, Part II

In the previous episode, I claimed that our temperature control application is utterly untestable. That’s quite a broad statement, so before going any further, I’d like to clarify what I really meant.

You can always load our code on an embedded device (like a Raspberry Pi) and attach an appropriate physical temperature sensor as well as a real heater. With the help of an additional external thermometer and an additional external heater you can create various conditions and check whether our system behaves as expected. This is called system testing. Even though it’s ultimately necessary, system testing is tedious, time-consuming, error-prone, expensive, depends on hardware and what-have-you. If you discover a bug at this stage, you have to repeat the whole manual procedure again (and again and again).

When I, as a developer, talk about testing, I generally mean unit testing. Unit testing can be done at your desk, on your PC, without external hardware, within seconds. Even better, it can be fully automated and parallelized. So to be more precise: our temperature control app is utterly impossible to unit-test.

What actually is the unit that we want to test? Our focus is on the control logic, so the class “Controller” is our unit-under-test (UUT). We want to verify whether all logic paths are covered and whether they produce the expected output (heater turned on or off).

In the remainder of this post, I’m going to rewrite the code such that unit testing becomes possible. Let’s go!

One major nuisance with the existing code is that it contains an endless loop. It’s impossible to look inside the loop from a unit test-case. So in a first step, I’ll factor out the meat of the loop, the code where the action takes place.

As you can see, all the loop in “run” now does is invoke a method called “step”. “step” contains the control logic and we have now a much better grip on it. By repeatedly calling “step” from within our test cases we can single step through the code logic and check after every step if we get the desired effects. (Therefore, I like the method name “step”, what about you?)

The fact that we aggregate the sensor and the heater as members of our controller is a gross violation of modern software design principles, as we make our code (the controller) depend on concrete implementations (classes). If we instead depend on interfaces, we can substitute one implementation (a hardware temperature sensor) with a fake/mock temperature sensor (more on mocks later), as long as they both adhere to the interface. So let’s get rid of concrete classes and introduce interfaces:

Our familiar hardware sensor implements this interface, just assume that the actual implementation hasn’t changed from the first episode:

We proceed in a similar fashion with the heater:

Finally, we can rewrite the controller code such that it works against abstract interfaces instead of concrete implementations:

All dependencies (sensor, heater) are passed via so-called dependency injection as interfaces to our controller class.

Obviously, in order to work as before, our application still must depend on concrete classes. But only in a single place — the main function:

Our code hasn’t changed a bit in functionality, it behaves just as before. But contrary to the initial design, it’s now utterly testable, er, unit-testable, I mean.

In the next episode, we’ll talk about mocks and finally implement the unit tests. Stay tuned!

Previous episode
Next episode

This Is Why We Mock, Part I

Everytime I watch one of those “This is why we ride” videos, I immediately want to jump on my bike and head for some twisty mountain roads. What’s funny is that after watching these videos, even people who don’t have a driver’s license feel a strong urge to jump on a bike.

I wrote this post in a similar spirit, hoping to convince C++ developers that unit testing with mocks is both, necessary and fun. My main focus is presenting a case for using mocks, since I regularly meet even experienced coders who are not familiar with mocks. If you’re not familiar with mocks, it’s a clear sign that you have never really unit-tested your code. It’s that simple! You might have unit-tested some free-standing library or algorithm that you wrote, but when an application consists of multiple interacting components implemented by different people, there’s no way for you to test your component in isolation without linking-in the components you depend on. If you do so, you’re not doing unit testing. You’re rather doing integration testing. And what do you do if the components you depend on are still under development? Do you postpone your work and wait until they are fully done? What if some components are hardware-dependent and only available on the target platform? Do you leave the comfort of your host platform and execute your tests only on the target platform? No! What you do is sprinkle some “design for testability” over your code and test against mocks. Suck it up!

Let’s do an example, a very basic temperature control application. It consists of a heat sensor, a heater, and the controller. The controller is a simplistic on-off controller which reads the current temperature from the sensor and compares it against a target temperature. If the temperature is below the target temperature, the controller turns the heater on, if the temperature is above the target temperature, it turns it off. To make it a bit more interesting, I added another requirement.

To avoid frequent on/off toggling of the heater around the target temperature point, the controller supports so-called hysteresis, a temperature range below the target temperature in which the controller doesn’t turn on the heater even though there is a deviation from the target temperature. Here’s an example: if the target temperature is set to 23 degrees and there’s a hysteresis of 2 degrees, the controller will not turn on the heat if the current temperature is 22 degrees. Not even if the current temperature is 21 degrees. It will, however, turn on the heater at 20 degrees (or less) as this is outside the range of the hysteresis.

But wait, there’s another point to mention. The temperature sensor provides a status, which tells if the temperature reading can be relied upon. If the sensor status is “bad”, the measurement must be ignored. If the status is “bad” for two times in a row, it means that the sensor is broken and the controller application shall (for the sake of safety) turn the heater off and exit.

If you like, you can pause here and think about how to implement and test this application.

Done? Here’s my naive implementation:

Does this code implement the requirements correctly? I don’t know for sure, but I assume it does. Do you agree?

Correct or buggy, it’s hard to agree. But we can easily agree on one thing: this code is utterly untestable. In the next part, I’ll refactor it such that it becomes one hundred percent testable, which allows us to move from correctness assumptions to knowledge. Stay tuned!

Next episode