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

C++ mock framework capable of mocking non-virtual methods and C functions

本文关键字:模拟 函数 C++ 框架 方法 虚拟      更新时间:2023-10-16

我知道这个问题的第一部分以前被问过,但那是很久以前的事了:)。我想知道,在模拟非虚拟方法和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()); 
}

GMock支持他们所说的用于模拟非虚拟方法的高性能依赖注入。

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

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,如果你在高级功能的第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)命名空间中的mock函数,因此测试代码调用T::time()而不是time()。这相当于使用抽象接口而不是具体类。

// rand_example.cpp
#include <time_mock.h>
int generateRandomNumber()
{
    return T::time( NULL ) * 3;
}

我过去在Cxxtest方法方面运气不错。

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

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

如果禁用内联并使用与位置无关的代码进行编译,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,这样您就可以用您想要的任何东西来替换它。函数/方法也可以用lambdas代替。

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