Stubs, Fakes and Mocks

Stubs, fakes and mocks are different unit testing techniques for implementing APIs that are consumed by code under test. The definitions of each term can be fluid from project to project. This page documents how the terms are used in the SysUnit framework.

Note: The example code on this page demonstrates various implementations of malloc(9). In practice overriding the behaviour of a critical function from libc in a unit test is a very poor idea. The real SysUnit implementation contains a mechanism to redirect calls to malloc(9) to a different function name, to avoid a name collision with malloc(3). For the purposes of these examples we're ignoring that issue, so that we can better focus on how to implement a KPI in SysUnit.

Stubs

A stub is an implementation whose behaviour never varies: frequently it will return a single, fixed value. Stubs are most frequently used when a code unit links against a symbol, but the subset of the unit that is being tested will never call the stub. Stubbing is frequently the simplest way to get a unit test to link. However, it typically is not a good way to implement any APIs used by code being tested, due to the inflexibility of the implementation. Using a stub is a signal that your test does not care about how the API is called, nor do you care about what values are returned by the API. If neither the parameters provided to the API nor its return value matter to the functionality of the code being tested, why does the code being tested call the API in the first place? On the other hand, if the use of the API is important to the functionality of the code under test, then it is usually important to verify the values passed to the API, and to test how the code reacts to a variety of return values.

In the specific case where a function is stubbed out because the test suite never expects the function to be called, it is best to implement the stub by calling abort(). That will cause the test to fail in an obvious way if the stub function is unexpectedly called.

Example

A possible stub implementation of malloc(9) would be:

   1 extern "C" void *
   2 malloc(size_t len, struct malloc_type *mtp, int flags)
   3 {
   4     abort();
   5 }

Fakes

A fake is an implementation whose behaviour varies based on the parameters passed by the code under test. It is a re-implementation of the logic of a function. In some cases it can be appropriate for a fake to only provide a subset of the functionality of the original API, so long as the subset implemented matches the needs of the code under test. The line between a stub and a fake is rather fuzzy, but typically if there is any logic in the implementation, it is more appropriate to call it a fake rather than a stub.

A fake is more complicated to implement than a stub, but it will be better able to emulate the original API. A fake is appropriate to use when you don't care about the specifics of how the code under test uses the API (e.g. you don't need to check the specific parameters passed) and you don't need precise control over the return values. A fake is likely overkill if the API will never be called from the subset of the code unit being tested (use a stub in this case). A fake may not be appropriate in cases where your test wants to validate the specific parameters passed to the API, or if you the API can fail in interesting ways that you want to emulate in your test.

Example

A possible fake implementation of malloc(9) would be:

   1 extern "C" void *
   2 malloc(size_t len, struct malloc_type *mtp, int flags)
   3 {
   4     void * mem = std::malloc(len);
   5     if (mem == NULL)
   6          return (mem);
   7 
   8     if (flags & M_ZERO)
   9         memset(mem, 0, len);
  10 
  11     return (mem);
  12 }

Mocks

A mock is an implementation whose behaviour is configurable by the test case. This is the most flexible way to implement an API for a unit test. The cost is that the mock is a little bit more complicated to actually write, and will require some initialization in each test case so that the mock knows what to do. A mock is most appropriate when different test cases require different functionality from the API, or you want to perform validation on the parameters passed to the mock. A mock would be overkill in situations where the same fake would suffice for every test case.

Mocks are generally not written by hand. Instead, a mocking framework library is used to define the behaviour of the mock. The mocking framework can expose generic configuration options (e.g. return value x when passed parameter y, fail the test if an argument does not match some criteria, etc). Individual mocks just need to be glue code between the specific function being mocked and the generic mocking framework.

SysUnit uses Google Mock as its mocking framework. Google Mock is a widely-used framework that offers a rich variety of possible mock behaviours. Its syntax is clean and easy to read, which makes test cases easier to follow. Google Mock does not offer native support for mocking C functions (it focuses on mocking C++ class methods). SysUnit provides a facility to ease the implementation of the glue code required to implement a mock version of a C function.

Example

A possible mock implementation of malloc(9) would be:

MockMalloc.h:

   1 #include "mock/GlobalMock.h"
   2 
   3 namespace SysUnit
   4 {
   5 class MockMalloc : public GlobalMock<MockMalloc>
   6 {
   7 public:
   8     MOCK_METHOD3(malloc, void *(size_t, struct malloc_type *, int));
   9 };
  10 }

MockMalloc.cpp:

   1 #include "MockMalloc.h"
   2 
   3 // This boilerplate causes MockMalloc::MockObj() to be initialized properly.
   4 namespace SysUnit
   5 {
   6 
   7 // The argument "0" is arbitrary and serves as a workaround for a strange C++ issue.
   8 template <>
   9 typename GlobalMock<MockMalloc>::Initializer GlobalMock<MockMalloc>::initializer(0);
  10 }
  11 
  12 extern "C" void *
  13 malloc(size_t size, struct malloc_type *mtp, int flags)
  14 {
  15     return SysUnit::MockMalloc::MockObj().malloc(size, mtp, flags);
  16 }


CategoryDocs

SysUnit/StubsFakesAndMocks (last edited 2019-03-28T10:58:16+0000 by TrevorRoydhouse)