4

I know the first part of this question has been asked before, but that was a long time ago :). I was wondering if the mean time any of the open source mocking frameworks caught up with Typemock Isolator++ when it comes to mocking non-virtual methods and C functions. I'm mostly interested in gcc under Linux. So far, I'm interested in mocking accessors (so that I can simulate states in the mocked object - see below) and replacing C functions from other libraries (select, pcap_*, etc.).

class Foo {
  public:
    ...
    bool IsCondition() { return condition; };
    ...
  private:
    bool condition;
}

// I want a framework that allows me to do something like this:
TEST(TestFoo) {
    MOCK_INTERFACE(Foo) mock_foo;
    EXPECT_CALL(mock_foo, IsCondition).returns(true);
    EXPECT(mock_foo.IsCondition()); 
}
Community
  • 1
  • 1
bruno nery
  • 2,022
  • 2
  • 20
  • 31
  • 1
    With C++11's variadic templates and perfect forwarding it looks like implementing what you're looking for ought to be *a lot* easier than it was previously. – Flexo Jan 17 '12 at 17:11

3 Answers3

5

GMock supports what they call hi-perf dependency injection for mocking non-virtual methods.

The gist, from the link above, is to use templates:

template <class PacketStream>
void CreateConnection(PacketStream* stream) { ... }

template <class PacketStream>
class PacketReader {
 public:
  void ReadPackets(PacketStream* stream, size_t packet_num);
};

Then you can use CreateConnection() and PacketReader in production code, and use CreateConnection() and PacketReader in tests.

For C functions they recommend interfaces, so probably not what you want. However, if you have separate libraries you could always link against a testing library, which contains functions with the same signatures as the deployment library. You could even do it dynamically with LD_PRELOAD if you were feeling particularly daring. That sounds like a lot of linking to me.

Cxxtest, if you look under section 8.1 in advanced features supports some macros to make using/creating interfaces easier:

From that link:

CXXTEST_MOCK_GLOBAL( time_t,        /* Return type          */  
                     time,          /* Name of the function */  
                     ( time_t *t ), /* Prototype            */
                     ( t )          /* Argument list        */ );

8.1.2. Mock Functions in Tested Code

The tested code uses mock global functions, rather than using the global functions directly. You access mock functions in the T (for Test) namespace, so the tested code calls T::time() instead of time(). This is the equivalent of using abstract interfaces instead of concrete classes.

// rand_example.cpp
#include <time_mock.h>

int generateRandomNumber()
{
    return T::time( NULL ) * 3;
}

I've had good luck with the Cxxtest approach in the past.

Paul Rubel
  • 26,632
  • 7
  • 60
  • 80
1

With a recent GCC (e.g. 4.6), you could write a plugin in C, or an extension in MELT for that purpose.

However, to customize GCC (by a plugin in C or an extension in MELT) you need to partly understand its internal representations (Gimple and Tree-s), which takes time (probably more than a week of work). So this approach make sense if you have a large enough code base to worth the effort.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

Provided you disable inlining and compile with position independent code, ELFSpy supports replacement of (member) functions.

Your test can be written as follows

// define alternate implementation for Foo::IsCondition
bool IsConditionTest(Foo*) { return true; }

int main(int argc, char** argv)
{
  // initialise ELFSpy
  spy::initialise(argc, argv);
  // create a handle for intercepting Foo::IsCondition calls
  auto method_spy = SPY(&Foo::IsCondition);
  // relink original method to different implementation
  auto method_fake = spy::fake(method_spy, &IsConditionTest); 
  // capture return value(s) from calls to Foo::IsCondition
  auto returned = spy::result(method_spy);
  // execute your test
  Foo foo;
  bool simulate = foo.IsCondition(); // calls IsConditionTest instead
  // check it worked
  assert(returned.value() == true);
}

The above example effectively relinks your code at runtime to call IsConditionTest instead of Foo::IsCondition, so you can replace it with whatever you want. Functions/Methods can also be replaced by lambdas.

See https://github.com/mollismerx/elfspy/wiki for full details. Disclaimer: I wrote ELFSpy.