【问题标题】:C++ mock framework capable of mocking non-virtual methods and C functions能够模拟非虚拟方法和 C 函数的 C++ 模拟框架
【发布时间】:2012-02-12 11:56:48
【问题描述】:

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

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

【问题讨论】:

  • 有了 C++11 的可变参数模板和完美的转发,看起来实现你正在寻找的东西应该比以前容易很多

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


【解决方案1】:

如果您禁用内联并使用与位置无关的代码进行编译,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。

【讨论】:

    【解决方案2】:

    GMock 支持他们所谓的mocking non-virtual methods 的高性能依赖注入。

    以上链接的要点是使用模板:

    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(用于测试)命名空间中的模拟函数,因此测试代码调用 T::time() 而不是 time()。这相当于使用抽象接口而不是具体类。

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

    过去我在使用 Cxxtest 方法时运气不错。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-07
        • 2014-10-25
        • 1970-01-01
        • 2016-09-28
        • 2011-12-19
        • 2012-07-29
        相关资源
        最近更新 更多