C ++使用Google Mock进行高性能单元测试? [英] C++ High performance unit testing with Google Mock?

查看:304
本文介绍了C ++使用Google Mock进行高性能单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Google Mock,我正在努力嘲笑C ++系统调用(特别是C ++ 11计时器函数)。

I'm using Google Mock, and I'm struggling to mock out C++ system calls (specifically the C++11 chrono functions).

知道我应该做一个接口,创建一个类来实现我的实际实现的接口,然后在我的测试模拟出接口。我试图编写一个嵌入式应用程序,所以这个间接级别对我来说太贵了。

I'm know I'm supposed to make an interface, create a class to implement the interface for my actual implementation, and then mock out the interface on my tests. I'm trying to write an embedded application, so this level of indirection sounds too expensive for me.

什么是最有效/将系统调用并入Google Mock?

What is the most efficient/performant way to incorporate system calls into Google Mock?

推荐答案

如果你在一个嵌入式环境中,一个虚拟分派的开销太大,或者编译器/链接器优化器那么你可以尝试以下3种方式来模拟平台调用。

If you're in an embedded environment where a virtual dispatch is too much overhead, or the compiler/linker optimizer for that architecture does a really bad job, then you can try the following 3 ways to mock the platform calls.

为了简单起见,假设你想要在 std :: this_thread 命名空间,例如 sleep_for(std :: milliseconds)

Assume for simplicity that you want to mock a function in the std::this_thread namespace, like sleep_for(std::milliseconds).

示例0 - 不可实现的基准

Example 0 - Untestable Baseline

没有嘲笑,让我们假设你的代码看起来像这样:

Without mocking, let's assume your code looks like this:

class untestable_class
{
public:

    void some_function()
    {
        if (must_sleep())
        {
            auto sleep_duration = std::chrono::milliseconds(1000);
            std::this_thread::sleep_for(sleep_duration);
        }
    }
};

你可以这样使用这个类:

You'd use that class like this:

void use_untestable_class()
{
    untestable_class instance;
    instance.some_function();
}

由于对标准库的依赖 sleep_for 函数,你有一个平台依赖,使得 some_function 难以进行单元测试,而实际上不进行集成测试。

Due to the dependency on the standard library sleep_for function, you have a platform dependency that makes some_function difficult to unit test without actually making an integration test out of it.

示例1 - 使用静态策略可测试

通过告诉我们的类使用特定的使用类模板,我们可以抽象出单元测试中的平台依赖关系。该策略可以是静态的也可以是实例 - 它们在运行时不需要虚拟分派,并且编译器/链接器可以轻松优化。

By telling our class to use a specific thread policy using a class template, we can abstract away the platform dependency in unit tests. The policy can either be static or instance - they both remove the need for a virtual dispatch at runtime and they are pretty easy for a compiler/linker to optimize.

static 政策案例,我们有一个依赖于平台的真实政策:

In the static policy case, we have one "real" policy that depends on the platform:

struct system_thread_policy1
{
    static void sleep_milliseconds(long milliseconds)
    {
        auto sleep_duration = std::chrono::milliseconds(milliseconds);
        std::this_thread::sleep_for(sleep_duration);
    }
};

我们还有一个模拟政策,我们可以在单元测试中控制:

We also have a "mock" policy that we can control in unit tests:

struct mock_thread_policy1
{
    // Mock attributes to verify interactions.
    static size_t sleep_milliseconds_count;
    static size_t sleep_milliseconds_arg1;

    // Resets the mock attributes before usage.
    static void sleep_milliseconds_reset()
    {
        sleep_milliseconds_count = 0;
        sleep_milliseconds_arg1 = 0;
    }

    static void sleep_milliseconds(size_t milliseconds)
    {
        sleep_milliseconds_count++;
        sleep_milliseconds_arg1 = milliseconds;
    }
};

// This is needed with MS compilers to keep all mock code in a header file.
__declspec(selectany) size_t mock_thread_policy1::sleep_milliseconds_count;
__declspec(selectany) size_t mock_thread_policy1::sleep_milliseconds_arg1;

使用策略的生产类将策略类型作为模板参数,并调用其 sleep_milliseconds statically:

The production class that uses the policy takes the policy type as a template parameter and calls into its sleep_milliseconds statically:

template <typename thread_policy>
class testable_class1
{
public:

    void some_function()
    {
        if (must_sleep())
        {
            thread_policy::sleep_milliseconds(sleep_duration_milliseconds);
        }
    }

private:

    enum { sleep_duration_milliseconds = 1000 };
};

在生产代码中, testable_class1 真实策略:

In production code, testable_class1 is instantiated using the "real" policy:

void use_testable_class1()
{
    testable_class1<system_thread_policy1> instance;
    instance.some_function();
}

在单元测试中, testable_class1 使用mock策略实例化:

In the unit test, testable_class1 is instantiated using the "mock" policy:

void test_testable_class1()
{
    mock_thread_policy1::sleep_milliseconds_reset();
    testable_class1<mock_thread_policy1> instance;
    instance.some_function();

    assert(mock_thread_policy1::sleep_milliseconds_count == 1);
    assert(mock_thread_policy1::sleep_milliseconds_arg1 == 1000);
    //assert("some observable behavior on instance");
}

此方法的上升趋势:


  • 测试互动的功能,例如上面的调用计数参数检查,可以添加到模拟中,类交互单元测试。

  • 静态调用使得优化器很容易将真实调用内联到 sleep_for

  • Features to test interactions, like the call count and argument checks above, can be added to the mock and be used to verify the class interactions unit tests.
  • The static call makes it very easy for the optimizer to inline the "real" call to sleep_for.

此方法的缺点:



  • 静态状态需要在使用它的每个单元测试中重置,因为不同的单元测试将改变该粘性状态。

  • 如果测试运行器将单元测试并行化,则静态状态使得不可能可靠地使用模拟器,因为不同的线程会干扰同一状态,从而导致不可预测的行为。

示例2 - 使用实例策略测试

>政策案例,我们有一个依赖于平台的真实政策:

In the instance policy case, we have one "real" policy that depends on the platform:

struct system_thread_policy2
{
    void sleep_milliseconds(size_t milliseconds) const
    {
        auto sleep_duration = std::chrono::milliseconds(milliseconds);
        std::this_thread::sleep_for(sleep_duration);
    }
};

我们还有一个模拟政策,我们可以在单元测试中控制:

We also have a "mock" policy that we can control in unit tests:

struct mock_thread_policy2
{
    mutable size_t sleep_milliseconds_count;
    mutable size_t sleep_milliseconds_arg1;

    mock_thread_policy2()
        : sleep_milliseconds_count(0)
        , sleep_milliseconds_arg1(0)
    {
    }

    void sleep_milliseconds(size_t milliseconds) const
    {
        sleep_milliseconds_count++;
        sleep_milliseconds_arg1 = milliseconds;
    }
};

使用策略的生产类将策略类型作为模板参数,策略注入到结构中并调用 sleep_milliseconds

The production class that uses the policy takes the policy type as a template parameter, gets an instance of the policy injected in the contructor and calls into its sleep_milliseconds:

template <typename thread_policy>
class testable_class2
{
public:

    testable_class2(const thread_policy& policy = thread_policy()) : m_thread_policy(policy) { }

    void some_function() const
    {
        if (must_sleep())
        {
            m_thread_policy.sleep_milliseconds(sleep_duration_milliseconds);
        }
    }

private:

    // Needed since the thread policy is taken as a reference.
    testable_class2(const testable_class2&);
    testable_class2& operator=(const testable_class2&);

    enum { sleep_duration_milliseconds = 1000 };

    const thread_policy& m_thread_policy;
};

在生产代码中, testable_class2 真正的策略:

In production code, testable_class2 is instantiated using the "real" policy:

void use_testable_class2()
{
    const testable_class2<system_thread_policy2> instance;
    instance.some_function();
}

在单元测试中, testable_class2 使用mock策略实例化:

In the unit test, testable_class2 is instantiated using the "mock" policy:

void test_testable_class2()
{
    mock_thread_policy2 thread_policy;
    const testable_class2<mock_thread_policy2> instance(thread_policy);
    instance.some_function();

    assert(thread_policy.sleep_milliseconds_count == 1);
    assert(thread_policy.sleep_milliseconds_arg1 == 1000);
    //assert("some observable behavior on instance");
}

此方法的上升趋势:


  • 测试互动的功能,例如上面的调用计数参数检查,可以添加到模拟中,

  • 实例调用使得优化器很容易将真实调用内联到 sleep_for

    • 在mock中没有静态状态,这使得写入,读取和维护单元测试更容易。

    • Features to test interactions, like the call count and argument checks above, can be added to the mock and be used to verify the class interactions unit tests.
    • The instance call makes it very easy for the optimizer to inline the "real" call to sleep_for.
      • There's no static state in the mocks, which makes writing, reading and maintaining the unit tests easier.

      此方法的缺点:



      • 实例状态向客户端添加了噪声( testable_class2 ) - 如果交互不需要验证,该策略可以通过构造函数中的值传递,大多数类goo都会消失。

      • The instance state adds mutable noise to the mock.
      • The instance state adds noise to the client (testable_class2) - if the interactions don't need verifying, the policy can be passed by value in the constructor and most of the class goo goes away.

      示例3 - 使用虚拟策略

      这与前两个示例的不同之处在于,它依赖于虚拟分派,但对编译器/链接器

      This differs from the first 2 examples in the way that this relies on virtual dispatch, but leaves a likely possibility for the compiler/linker to optimize the virtual dispatch away if it can detect that the instance operated on is of base type.

      首先,我们有生产 base 类在非纯虚函数中使用真实策略:

      First, we have the production base class that uses the "real" policy in a non-pure virtual function:

      class testable_class3
      {
      public:
      
          void some_function()
          {
              if (must_sleep())
              {
                  sleep_milliseconds(sleep_duration_milliseconds);
              }
          }
      
      private:
      
          virtual void sleep_milliseconds(size_t milliseconds)
          {
              auto sleep_duration = std::chrono::milliseconds(milliseconds);
              std::this_thread::sleep_for(sleep_duration);
          }
      
          enum { sleep_duration_milliseconds = 1000 };
      };
      

      其次,我们有实现mock策略的派生类在虚函数(一种模板方法设计模式):

      Second, we have the derived class that implements a "mock" policy in the virtual function (a kind of Template Method design pattern):

      class mock_testable_class3 : public testable_class3
      {
      public:
      
          size_t sleep_milliseconds_count;
          size_t sleep_milliseconds_arg1;
      
          mock_testable_class3()
              : sleep_milliseconds_count(0)
              , sleep_milliseconds_arg1(0)
          {
          }
      
      private:
      
          virtual void sleep_milliseconds(size_t milliseconds)
          {
              sleep_milliseconds_count++;
              sleep_milliseconds_arg1 = milliseconds;
          }
      };
      

      在生产代码中, testable_class3 作为本身:

      In production code, testable_class3 is just instantiated as itself:

      void use_testable_class3()
      {
          // Lots of opportunities to optimize away the virtual dispatch.
          testable_class3 instance;
          instance.some_function();
      }
      



      在单元测试中, testable_class3 使用mock派生类实例化:

      In the unit test, testable_class3 is instantiated using the "mock" derived class:

      void test_testable_class3()
      {
          mock_testable_class3 mock_instance;
          auto test_function = [](testable_class3& instance) { instance.some_function(); };
          test_function(mock_instance);
      
          assert(mock_instance.sleep_milliseconds_count == 1);
          assert(mock_instance.sleep_milliseconds_arg1 == 1000);
          //assert("some observable behavior on mock_instance");
      }
      

      此方法的上升趋势:


      • 测试互动的功能,例如上面的调用计数参数检查,可以添加到模拟中,

      • 对本身的基类虚拟调用使优化器可以将真实调用并入 sleep_for

      • 在模拟中没有静态状态,这使得写入,读取和维护单元测试更容易。

      • Features to test interactions, like the call count and argument checks above, can be added to the mock and be used to verify the class interactions unit tests.
      • The base class virtual call on "itself" makes it possible for the optimizer to inline the "real" call to sleep_for.
      • There's no static state in the mocks, which makes writing, reading and maintaining the unit tests easier.

      此方法的缺点:


      • 基类不能标记为 final (C ++ 11),因为它必须被允许继承,如果比上面的简单例子更复杂,这可能会影响类设计的其余部分。

      • 编译器/链接器可能欠完善或根本无法优化虚拟分派。

      • The base class cannot be marked final (C++11), since it must be allowed to be inherited from, and this might affect the rest of the class design if there is more complexity than the simple example above.
      • The compiler/linker might be subpar or simply unable to optimize the virtual dispatch.

      / strong>

      Test Run

      以上所有内容均可通过以下方式进行测试:

      All of the above can be tested with this:

      int _tmain(int argc, _TCHAR* argv[])
      {
          test_testable_class1();
          test_testable_class2();
          test_testable_class3();
      
          return 0;
      }
      

      ,完整的可运行示例为 http://pastebin.com/0qJaQVcD

      and the complete runnable example is at http://pastebin.com/0qJaQVcD

      这篇关于C ++使用Google Mock进行高性能单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆