Mockable interfaces for easy testing.

This article is part of a series of articles on programming idioms.

Mockable interfaces are essentially the idiom I described as dependency injection using interfaces.

Instantiation

You break some business logic functionality between a number of objects that depend on each other. One of such classes might be accessing a database (buzz), another might be used to read data from a file (bar), and yet another (foo) might use both of them to import data from the file into the database.

Somewhere in your code you declare these objects and chain them together, effectively topologically sorting the dependency graph, relying on the C++ class lifetime for orderly construction and destruction:

1
2
3
4
5
6
7
8
int main() {
  buzz z;
  bar y;
  foo x{ y, z, "flintstone" };
  waldo w{ x, y };

  w.do_something();
}

Virtual function interfaces

In order to be able to test these objects in isolation of each other, you use interfaces of pure virtual functions, then derive from those interface: once with a real implementation and once with a mock one for testing.

Using inheritance raises the question: if someone destroys the object through the interface base class, what ensures that the derived destructor is called? There are two approaches.

Ensure that the derived class destructor is called when the base class destructor is called. This is achieved by adding a virtual destructor (not pure) to the base class.

1
2
3
4
5
6
struct foo_interface {
  virtual void some_fn() = 0;
  virtual int some_fn2() = 0;

  virtual ~foo_interface() = default;
};

The second alternative is to ensure that you can’t destroy though the base class instance in the first place. This is achieved by hiding (protected) the base class destructor.

1
2
3
4
5
6
7
struct foo_interface {
  virtual void some_fn() = 0;
  virtual int some_fn2() = 0;

protected:
  ~foo_interface() = default;
};

If you go for this second option then you might want to make the derived class final so that itself can’t be a base class (or do a protected destructor again for similar reasons).

1
2
3
class foo final: public foo_interface {
  void some_fn() override;
  int some_fn2() override;

Real implementation

You will have an implementation for such an interface:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class foo : public foo_interface {
  bar_interface & bar_;
  buzz_interface & buzz_;
  const std::string fred_;

public:
  foo(bar_interface & bar,
      buzz_interface & buzz,
      const std::string & fred):
    bar_{ bar }, buzz_{ buzz }, fred_{ fred }
  {}

  foo(const foo &) = delete;
  foo & operator=(const foo &) = delete;

  // implement in terms of bar_, buzz_ and fred_
  void some_fn() override;
  int some_fn2() override;
};

A topmost class, e.g. waldo, does not need to be inherited from an interface.

Capturing dependencies

In this case foo depends on a couple of other interface based classes. We store references to these interfaces. It’s best to capture references to these dependencies at construction time into the member variables. Another alternative is to set them later via an init method (or equivalent), but that would have lifetime issues (e.g. they are not correct/or available between construction and init.

Notice that we stored the interfaces in an almost mechanical/recipe fashion, we did not use them to create another object. That adds complexity, e.g. we’ll see later that a unit test would also cover that object, so it should be done where really there is some gain to be had.

The string fred is another customization that is captured at construction time. It might be something like a database connection string. We could create an interface for that for more complex cases or simply store a copy of the connection string in a member variable like above.

Dangling references risk

1
2
  foo(const foo &) = delete;
  foo & operator=(const foo &) = delete;

Holding references creates questions about ensuring they are not dangling. If we don’t plan to copy or move any of these objects that would address the issue. Deleting the copy constructor and assignment means this class won’t be copied or moved. The language rules are that if the copy operations are deleted, the move one are also implicitly deleted. We take advantage of these rules to be less verbose (Observation: most developers forget to put noexcept for move constructor and assignment).

We do this for all the classes involved with references (foo, bar, buzz and waldo).

As we’ll see later using smart pointers instead of references is not a good idea because it introduces more problems that it fixes.

Note: we considered deleting the copy constructor and assignment in the interface. Putting it there would have rhymed with the “Rule of 3”: if you have a destructor also deal with copy constructor and assignment. That is a sound starting point for types that explicitly release a resource in the destructor. However for the interfaces the reason we have a destructor has to do with calling the right destructor, not with handling resources, it’s a different reason, we’re defaulting it anyway. We thought that deleting the copy constructor and assignment in the class (rather than in the interface which only deals with the destructor) makes it easier to do local reasoning. To check what’s done to avoid the potential issue of dangling references (which classes use), look at the class using the references and the class that implements the interface stored as reference and the instantiation site, without also having to check their base class interface definition. Also some like waldo might not be derived from an interface anyway, but still would benefit from knowing it can’t be returned by accident from a function and have dangling references to objects from within the function scope.

Testing foo

It’s easy to create mocks for behaviour verification using a test framework, like Google test, and test the classes that take their dependencies as interfaces.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class bar_mock :
  public bar_interface
{
public:
  MOCK_METHOD(void, bar_fn, (), (override));
};

// ... `buzz_mock

TEST(foo_test, trivial)
{
  StrictMock<bar_mock> y;
  EXPECT_CALL(y, bar_fn())
    .Times(1);
  StrictMock<buzz_mock> z;

  foo x{ y, z, "bedrock" };
  x.some_fn();
}

You would usually use something like the Google test StrictMock to ensure that unexpected calls fail the test, that’s the point of behaviour verification.

We used the mocks to isolate a smaller number of componets that are involved in the test, but note that the “unit test” still involves other components (std::string at least in this case):

waldo foo buzz bar std::string heap mocks spy area tested

Testing bar and buzz

bar and buzz do not have interfaces injected at construction time. They often either:

  • just do data manipulation: in which case they can be tested as in the regular data and functions idiom
  • just call C APIs: in which case they can be tested like the C API wrappers

Builder class

Occasionally you can’t use the stack directly for instantiation, so then you use a builder class that relies on the same C++ class lifetime rules for orderly construction and destruction:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class waldo_builder {
  buzz z_;
  bar y_;
  foo x_;
  waldo w_;
public:
  waldo_builder() :
    z_{},
    y_{},
    x_{ y_, z_, "flintstone" },
    w_{ x_, y_ }
  {}

  void do_something() {
    w_.do_something();
  }
};

Then you would instantiate that waldo_builder once, e.g. using a std::unique_ptr. This class would not be unit tested, though it’s simple functionality might be covered by component or system tests.

Why this idiom works?

Mock interfaces are well supported by unit test frameworks like Google test, both their creation using MOCK_METHOD macro and test primitives like EXPECT_CALL.

The compiler might remove the usage of the virtual table, something called devirtualization. In the instantiation example at the top it is often clear to the compiler that foo calls a method of bar, so it does not need to store a reference to the interface and do the virtual table indirection, removing the storage and call overheads.

These classes are often “doer classes”, i.e. classes that are there for doing things rather than “data classes”, e.g. reading data from a file, accessing a database, while at the same time they have named methods for doing things, being less refined than function objects that do things via the function operator operator() which they implement.

Java Spring users might notice analogies with @Service and @Component annotations. The diference is that here the dependency injection was done by hand.

What are it’s limits?

Of the three idioms presented here, this one is the least intellectually sound one.

In it’s defence we can say that it is useful in the context of lack of compiler/build tools support for intrusive testing where you want to substitute entities for testing purposes with minimal code bloat complexity. For example even if there is some code bloat in the declaration and use of the interface, including declaring the same function 3 times (interface, implementation and mock), the compiler will catch many divergence errors (e.g. implementation does not implement interface method) at compile time.

It is a direct blast from the Object Oriented Programming of the early 90s. Many an enthusiastic programmer has created an overly complex and inefficient program after reading the GoF patterns book. For issues around inheritance, see talks like Sean Parent’s talk “Inheritance is the Base Class of Evil”. To keep the idiom usable you need to take active steps to avoid it’s pitfalls.

Avoid the factory pattern where an interface returns another interface. Usually that requires additional unnecesary memory allocations. Return data from interface methods if possible.

Create abstractions at the right level. Avoid both over fragmented tiny classes with absolutely no real functionality. See the usage of a std::string fred above in order to avoid an interface with one method returning a hardcoded string. Also avoid classes that depend on a large number of interfaces in the constructor with complex code to orchestrate them. E.g. you can have a router class that routes data to a large number of interfaces if the code is simple (e.g. a large switch that does the dispatch). Otherwise as a rule of thumb about 7 dependent interfaces is usually too much, with a sweet spot of between 3 and 5 dependent interfaces.

Avoid deep or wide inheritance hierarchies. This introduces a lot of coupling making it hard for the code to evolve gradually. It’s also hard when debugging you see that some interface method is called, it’s not easy to see which class it belongs especially where there might be more than one candidate.

Not all the classes above are testable with pure unit tests. The ones in the middle of the dependency graph are, like foo and waldo. Others, like waldo_builder, are simple and would be covered by component/system tests, it’s hard and little value from trying to unit tests. Yet others like buzz and bar might use regular data or C wrapper APIs and should follow a similar strategy like those.

And lastly there are misunderstanding around the alternatives like usages of smart pointers and const for holding to the interfaces. In the next article we’ll have a look at const.

References

For ideas on using dependency injection at construction time in Java (and some allusions to value/regular data types) see The Clean Code Talks - “Global State and Singletons” by Miško Hevery (13 Nov 2008).