Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fake/mock nonvirtual C++ methods

It known that in C++ mocking/faking nonvirtual methods for testing is hard. For example, cookbook of googlemock has two suggestion - both mean to modify original source code in some way (templating and rewriting as interface).

It appear this is very bad problem for C++ code. How can be done best if you can't modify original code that needs to be faked/mocked? Duplicating whole code/class (with it whole base class hierarchy??)

like image 795
zaharpopov Avatar asked Nov 25 '10 16:11

zaharpopov


3 Answers

One way that we sometimes use is to split the original .cpp file into at least two parts.

Then the test apparatus can supply its own implementations; effectively using the linker to do the dirty work for us.

This is called the "Link Seam" in some circles.

like image 106
sdg Avatar answered Oct 16 '22 10:10

sdg


I followed the Link Seam link from sdg's answer. There I read about different types of seams, but I was most impressed by Preprocessing Seams. This made me think about exploiting further the preprocessor. It turned out that it is possible to mock any external dependency without actually changing the calling code.

To do this, you have to compile the calling source file with a substitute dependency definition. Here is an example how to do it.

dependency.h

#ifndef DEPENDENCY_H
#define DEPENDENCY_H

class Dependency
{
public:
    //...
    int foo();
    //...
};

#endif // DEPENDENCY_H

caller.cpp

#include "dependency.h"

int bar(Dependency& dependency)
{
    return dependency.foo() * 2;
}

test.cpp

#include <assert.h>

// block original definition
#define DEPENDENCY_H

// substitute definition
class Dependency
{
public:
    int foo() { return 21; }
};

// include code under test
#include "caller.cpp"

// the test
void test_bar()
{
    Dependency mockDependency;

    int r = bar(mockDependency);

    assert(r == 42);
}

Notice that the mock does not need to implement complete Dependency, just the minimum (used by caller.cpp) so the test can compile and execute. This way you can mock non-virtual, static, global functions or almost any dependency without changing the productive code. Another reason I like this approach is that everything related to the test is in one place. You don't have to tweak compiler and linker configurations here and there.

I have applied this technique successfully on a real world project with big fat dependencies. I have described it in more detail in Include mock.

like image 12
Peter Dotchev Avatar answered Oct 16 '22 10:10

Peter Dotchev


Code has to be written to be testable, by whatever test techniques you use. If you want to test using mocks, that means some form of dependency injection.

Non-virtual calls with no dependence on a template parameter pose the same problem as final and static methods in Java[*] - the code under test has explicitly said, "I want to call this code, not some unknown bit of code that's dependent in some way on an argument". You, the tester, want it to call different code under test from what it normally calls. If you can't change the code under test then you, the tester, will lose that argument. You might as well ask how to introduce a test version of line 4 of a 10-line function without changing the code under test.

If the class to be mocked is in a different TU from the class under test, you can write a mock with the same name as the original and link that instead. Whether you can generate that mock using your mocking framework in the normal way, I'm not so sure.

If you like, I suppose it's a "very bad problem for C++" that it's possible to write code that's hard to test. It shares this "problem" with a great number of other languages...

[*] My Java knowledge is quite low-power. There may be some clever way of mocking such methods in Java, which aren't applicable to C++. If so, please disregard them in order to see the analogy ;-)

like image 7
Steve Jessop Avatar answered Oct 16 '22 10:10

Steve Jessop