C ++模拟框架,能够模拟非虚方法和C函数

时间:2012-01-17 17:05:30

标签: c++ linux unit-testing gcc mocking

我知道这个问题的第一部分是asked before,但那是很久以前的事了:)。我想知道在模拟非虚拟方法和C函数时,任何开源模拟框架的平均时间是否赶上了Typemock Isolator ++。我最感兴趣的是Linux下的gcc。到目前为止,我对模拟访问器感兴趣(这样我可以模拟模拟对象中的状态 - 见下文)并从其他库中替换C函数(select,pcap_ *等)。

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

3 个答案:

答案 0 :(得分:5)

GMock支持mocking non-virtual methods所谓的hi-perf依赖注入。

从上面的链接中,要点是使用模板:

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

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

然后,您可以在生产代码中使用CreateConnection()和PacketReader,并在测试中使用CreateConnection()和PacketReader。

对于C函数,他们推荐接口,所以可能不是你想要的。但是,如果您有单独的库,则始终可以链接到测试库,该测试库包含与部署库具有相同签名的功能。你甚至可以用LD_PRELOAD动态地做它,如果你感觉特别大胆的话。这听起来像很多链接到我。

Cxxtest,如果您在advanced features的第8.1节中查看,则支持使用某些宏来更轻松地使用/创建界面:

从该链接:

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
  

测试代码使用模拟全局函数,而不是直接使用全局函数。您可以在T(for Test)命名空间中访问模拟函数,因此测试的代码调用T :: time()而不是time()。这相当于使用抽象接口而不是具体类。

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

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

过去我对Cxxtest方法运气不错。

答案 1 :(得分:0)

使用最近的GCC(例如4.6),您可以在C中编写插件,或者为MELT编写扩展名。

但是,要自定义GCC(通过C中的插件或MELT中的扩展),您需要部分理解其内部表示(Gimple和Tree-s),这需要时间(可能超过一周的工作)。因此,如果你有足够大的代码库值得付出努力,这种方法就有意义了。

答案 2 :(得分:0)

如果您禁用内联并使用与位置无关的代码进行编译,则ELFSpy支持替换(成员)函数。

您的测验可以写成如下

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

以上示例在运行时有效地重新链接了代码,以调用IsConditionTest而不是Foo :: IsCondition,因此您可以将其替换为所需的任何内容。函数/方法也可以用lambda代替。

有关详细信息,请参见https://github.com/mollismerx/elfspy/wiki。免责声明:我写了ELFSpy。