27

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??)

casperOne
  • 73,706
  • 19
  • 184
  • 253
zaharpopov
  • 16,882
  • 23
  • 75
  • 93
  • There're some technics that handles nonvirutal methods. See https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#mocking-nonvirtual-methods – Sungguk Lim Oct 09 '17 at 09:35
  • The above link is broken, and the current GMock docs techniques all require some modification of the code under test. C folks have been doing this for years using [linker substitutions](http://blog.wingman-sw.com/linker-substitution-in-c-limitations-and-workarounds) (AKA Link seams). It's the only elegant way to do this IMO. OTOH, the [performance hit of using virtual functions](https://johnysswlab.com/the-true-price-of-virtual-functions-in-c/) may not be so bad as people think. – Leonardo Oct 26 '22 at 15:40

8 Answers8

13

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.

sdg
  • 4,645
  • 3
  • 32
  • 26
  • horrible code duplication. think if the class has base classes! – zaharpopov Nov 25 '10 at 16:32
  • @zaharpopov it does duplicate the interfaces, but not the entire class contents. Because presumably the test version of the code has completely different "guts". – sdg Nov 25 '10 at 16:35
  • ++ for the link to the book chapter - very interesting – zaharpopov Nov 25 '10 at 18:46
  • 2
    +1 - we did that too. One foobar.cpp with the actual implementation and one foobar_ut.cpp with the mock stubs for the unit tests. – Martin Ba Nov 28 '10 at 20:45
  • Leave the declarations in the class header file and move all definitions to a source file. Use dynamic linking to provide the fake implementations. – Leonardo Oct 26 '22 at 15:43
12

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.

Community
  • 1
  • 1
Peter Dotchev
  • 2,980
  • 1
  • 25
  • 19
  • 1
    The problem with this method is that you can no longer test the original "Dependency" class in the same project. – MyUsername112358 Nov 02 '16 at 11:26
  • 3
    You can test it in another compilation unit, e.g. in test2.cpp – Peter Dotchev Dec 05 '16 at 07:56
  • But how could we apply this if caller.cpp and denpendency.cpp and more cpp be packed in a same library? It may be leads to voliate One Definition Rule. I asked a question here. https://stackoverflow.com/questions/59191738/how-the-code-run-when-exist-override-ambigous-function Could you give any suggestions? – Xu Hui Dec 05 '19 at 14:30
  • Normally you don't pack tests in a library. Compile and run each test independently. – Peter Dotchev Mar 06 '20 at 13:48
  • You can leave the class declarations in the header and have all de method definitions (i.e. implementations) in a source file. Compile your SW to be tested as a dynamic library and provide the mock definitions in your test source files. The linker will do the rest for you. – Leonardo Oct 26 '22 at 15:42
  • @MyUsername112358 if you want to test the original Dependency class, you won't mock it in the first place. I guess this test is only to test whether the caller code is calling the Dependency correctly. Only problem is API change that might break your tests. As long as you remember to adjust your caller to the new API along with the test, there should be no problem – Mehdi Aug 08 '23 at 17:51
7

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 ;-)

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • sounds sad but true what you say here, but suppose i have legacy code, so there is absolutely **no way** to mock it? – zaharpopov Nov 25 '10 at 17:49
  • 1
    @zaharpopov: then you'll have to test it some other way. Have you considered functional/acceptance testing? ;-) – Steve Jessop Nov 25 '10 at 21:18
2

I think it is not possible to do it with standard C++ right now (but lets hope that a powerful compile-time reflection will come to C++ soon...). However, there are a number of options for doing so.

You might have a look at Injector++. It is Windows only right now, but plans to add support for Linux & Mac.

Another option is CppFreeMock, which seems to work with GCC, but has no recent activities.

HippoMocks also provide such ability, but only for free functions. It doesn't support it for class member functions.

I'm not completely sure, but it seems that all the above achieve this with overwriting the target function at runtime so that it jumps to the faked function.

The there is C-Mock, which is an extension to Google Mock allowing you to mock non-virtual functions by redefining them, and relying on the fact that original functions are in dynamic libraries. It is limited to GNU/Linux platform.

Finally, you might also try PowerFake (for which, I'm the author) as introduced here.

It is not a mocking framework (currently) and it provides the possibility for replacing production functions with test ones. I hope to be able to integrate it to one or more mocking frameworks; if not, it'll become one.

Update: It has an integration with FakeIt.

Update 2: Added support for Google Mock

It also overrides the original function during linking (so, it won't work if a function is called in the same translation unit in which it is defined), but uses a different trick than C-Mock as it uses GNU ld's --wrap option. It also needs some changes to your build system for tests, but doesn't affect the main code in any way (except if you are forced to put a function in a separate .cpp file); but support for easily integrating it into CMake projects is provided.

But, it is currently limited to GCC/GNU ld (works also with MinGW).

Update: It supports GCC & Clang compilers, and GNU ld & LLVM lld linkers (or any compatible linker).

hedayat
  • 184
  • 1
  • 10
1

@zaharpopov you can use Typemock IsolatorPP to create mocks of non-virtual class and methods without changing your code (or legacy code). for example if you have a non-virtual class called MyClass:

class MyClass
{
 public:
   int GetResult() { return -1; }
}

you can mock it with typemock like so:

MyClass* fakeMyClass = FAKE<MyClass>();
WHEN_CALLED(fakeMyClass->GetResult()).Return(10);

By the way the classes or methods that you want to test can also be private as typemock can mock them too, for example:

class MyClass
{
private:
   int PrivateMethod() { return -1; }
}


MyClass* myClass =  new MyClass();

PRIVATE_WHEN_CALLED(myClass, PrivateMethod).Return(1);

for more information go here.

JamesR
  • 745
  • 4
  • 15
0

You very specifically say "if you can't modify original code", which the techniques you mention in your question (and all the other current "answers") do.

Without changing that source, you can still generally (for common OSes/tools) preload an object that defines its own version of the function(s) you wish to intercept. They can even call the original functions afterwards. I provided an example of doing this in (my) question Good Linux TCP/IP monitoring tools that don't need root access?.

Community
  • 1
  • 1
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • So what? It will also work for C++. At the linker stage, it's all the same. – Tony Delroy Nov 25 '10 at 16:51
  • Isnt it very harder in C++ because name mangling - remember this is not simple function loaded with dynamic load, but a whole class with base classes – zaharpopov Nov 25 '10 at 17:48
  • 1
    No - I've done it before and it's easy (in fact, I think I answered a question somewhere with an example I'd thrown together and tested, but it was much harder to find than the only question I've asked on SO ;-P). You simply put the overriding functions into a file that opens the same namespaces, declares the same class, then only add the methods you want to override. Of course, it won't work for inlined functions (or virtual functions, but they're excluded from your question), but non-virtual out-of-line's no problem. The point: the same compiler still mangles the same way. – Tony Delroy Nov 25 '10 at 23:18
-1

That is easier then you think. Just pass the constructed object to the constructor of the class you are testing. In the class store the reference to that object. Then it is easy to use mock classes.

EDIT :

The object that you are passing to the constructor needs an interface, and that class store just the reference to the interface.


struct Abase
{
  virtual ~Abase(){}
  virtual void foo() = 0;
};

struct Aimp : public Abase
{
  virtual ~Aimp(){}
  virtual void foo(){/*do stuff*/}
};

struct B
{
  B( Aimp &objA ) : obja( objA )
  {
  }

  void boo()
  { 
    objA.foo();
  }

  Aimp &obja;
};


int main()
{
//...
Aimp realObjA;
B objB( realObjA );
// ...
}

In the test, you can pass the mock object easy.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • sorry I completely don't understand what you said. can you explain more and give example? – zaharpopov Nov 25 '10 at 16:34
  • so you're saying, "modify the code under test, to make it testable"? – Steve Jessop Nov 25 '10 at 16:38
  • @Steve No, I am saying "practice the TDD", and your code will be tested. – BЈовић Nov 25 '10 at 16:40
  • 3
    all very well, but the question says that can't be done here. The questioner isn't writing the code that's under test. So, *given that it doesn't use interfaces*, how are you going to test it? As you see from my answer, I think the questioner is basically out of luck, so I think it's just taunting to say, "well, if the code was written differently in the first place then it would be testable" ;-) – Steve Jessop Nov 25 '10 at 16:43
  • @Steve Oh well. I missed that small thing. In that case, the answer is "very hard, if not impossible". – BЈовић Nov 25 '10 at 16:46
-2

I used to create an interface for the parts I needed to mock. Then I simply created a stub class that derived from this interface and passed this instance to my classes under test. Yes, it is a lot of hard work, but I found it worth it for some circumstances.

Oh, by interface I mean a struct with only pure virtual methods. Nothing else!

Daniel Lidström
  • 9,930
  • 1
  • 27
  • 35
  • I don't get it. I have concrete class I want to fake, and can't change its code. How can it fake this class with method you propose? – zaharpopov Nov 25 '10 at 16:28
  • You'll need to extract its public interface. Then the code you have under test will need to use this interface instead of your concrete class. This will allow you to swap out the implementation with your stub/mock. – Daniel Lidström Nov 25 '10 at 16:35
  • 1
    Yes you need to change it to not only rely on concrete classes. This is the main way to make code testable, to depend on interfaces instead of concrete classes. – Daniel Lidström Nov 25 '10 at 16:38
  • 2
    Daniel, sorry then but it not answer question - please read it again it says "if you can't modify original code " – zaharpopov Nov 25 '10 at 17:48
  • Daniel, also, what you suggest will require the use a virtual methods. – Tomer W Jun 23 '14 at 16:32