21

I am a fairly new software developer currently working adding unit tests to an existing C++ project that started years ago. Due to a non-technical reason, I'm not allowed to modify any existing code. The base class of all my modules has a bunch of methods for Setting/Getting data and communicating with other modules.

Since I just want to unit testing each individual module, I want to be able to use canned values for all my inter-module communication methods. I.e. for a method Ping() which checks if another module is active, I want to have it return true or false based on what kind of test I'm doing. I've been looking into Google Test and Google Mock, and it does support mocking non-virtual methods. However the approach described (https://google.github.io/googletest/gmock_cook_book.html#MockingNonVirtualMethods) requires me to "templatize" the original methods to take in either real or mock objects. I can't go and templatize my methods in the base class due to the requirement mentioned earlier, so I need some other way of mocking these virtual methods

Basically, the methods I want to mock are in some base class, the modules I want to unit test and create mocks of are derived classes of that base class. There are intermediate modules in between my base Module class and the modules that I want to test.

I would appreciate any advise!

Thanks,

JW

EDIT: A more concrete examples

My base class is lets say rootModule, the module I want to test is leafModule. There is an intermediate module which inherits from rootModule, leafModule inherits from this intermediate module.

In my leafModule, I want to test the doStuff() method, which calls the non virtual GetStatus(moduleName) defined in the rootModule class. I need to somehow make GetStatus() to return a chosen canned value. Mocking is new to me, so is using mock objects even the right approach?

DerKasper
  • 167
  • 2
  • 11
wk1989
  • 611
  • 1
  • 8
  • 19
  • Can you locally modify the code just for your tests? You wouldn't have to check-in the changes. –  Feb 26 '10 at 05:03
  • Are you required to use unit tests? There are other (automated) methods of regression testing. Unit testing is a tool, not a religion - if it doesn't fit, don't use it. – ima Feb 26 '10 at 05:15
  • Ideally my team would like to use unit tests, seeing as our module tree is rather large, we would really want to decouple the testing of inter-module functionality from the testing of a module's local methods. It is unfortunate though that our previous design decisions have made unit testing somewhat of a challenge. What other methods of regression testing are you referring to? I imagine that, using other testing methodologies we'll still need some sort a way to have non-virtual methods set or return canned values, meaning that we'll either find a workaround or keep another source tree around. – wk1989 Feb 26 '10 at 05:38
  • +1 for the Google Mocks cookbook link. I have exactly this problem, but with "templatized" methods already part of my design, so it sounds like it could be ideal for me. – JBentley Mar 11 '13 at 04:15

3 Answers3

14

There are some different ways of replacing non-virtual functions. One is to re-declare them and compile a new test executable for each different set of non-virtual functions you'd like to test. That's hardly scaleable.

A second option is to make them virtual for test. Most compilers allow you to define something on the command-line so compile your code with -DTEST_VIRTUAL=virtual or -DTEST_VIRTUAL to make them either virtual or normal depending on whether or not it's under test or not.

A third option which may be usable is to use a mocking framework that lets you mock non-virtual functions. I'm the author of HippoMocks (disclaimer with regard to neutrality and so on) and we've recently added the ability to mock plain C functions on X86 platforms. This can be extended to non-virtual member functions with a bit of work and would be what you're looking for. Keep in mind that, if your compiler can see both the use and the definition of a function at one time that it may inline it and that the mocking may fail. That holds in particular for functions that are defined in headers.

If regular C function mocking is sufficient for you, you can use it as it is now.

dascandy
  • 7,184
  • 1
  • 29
  • 50
  • 1
    I've just come across this answer. I've been intending to use HippoMocks for a while now (first time using a mocking framework), but I'm leaning towards compile-time dependency injection using templates, and I wasn't sure if HippoMocks can manage this. Can you explain a bit further what would be required to mock non-virtual member functions? And why would inlining cause the mocking to fail? – JBentley Mar 11 '13 at 04:13
  • Non-virtual member functions may be inlined by either the compiler during the compile of a unit, or by the linker at link time (LTO). Both of these mean that the "address" of a function is no longer well-defined, so replacing it with something else is also impossible as you can't find those other "new" locations. For virtual member functions you're guaranteed this doesn't happen (as they're virtual, there's no inlining possible). – dascandy Mar 12 '13 at 09:49
  • Non-virtual functions are mocked by replacing the first N bytes of the function - literally hacking the code at runtime - with a jump to a mock function. This only works if there's just one location of the function to edit; you can't have a function pointer to multiple locations. Answer split in two commments as they're length-limited. – dascandy Mar 12 '13 at 09:50
  • I used `preprocessor directives` to do so. – duong_dajgja Jul 04 '17 at 08:53
3

I would write a Perl/Ruby/Python script to read in the original source tree and write out a mocked source tree in a different directory. You don't have to fully parse C++ in order to replace a function definition.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
  • 3
    That would probably work, however it would be simpler IMO to just have 2 source trees and in one of them change the base class methods to be virtual. There would be some downsides affecting the guys that are in charge of the source control system. Developers will also have to adjust themselves to writing code for 2 working revisions. What I'm looking for is a workaround that'll let me integrate my unit tests into a single source tree that can be built (and executed) in one pass. What I'm not sure is if this is possible. – wk1989 Feb 26 '10 at 05:56
  • 1
    @wk1989: Developers would never work on two source trees with the script idea. That is the whole point. All code editing would be done on the real code tree. Tests would be run off the script generated test tree. – Zan Lynx Feb 26 '10 at 16:03
2

One approach would be to specify different sources for testing. Say your production target uses rootModule.h and rootModule.cpp. Use different sources for your testing target. You can specify a different header by changing your include path, so that #include "rootModule.h" actually loads unittest/rootModule.h. Then mock rootModule to your heart's content.

Melebius
  • 6,183
  • 4
  • 39
  • 52
Jon Reid
  • 20,545
  • 2
  • 64
  • 95