C ++中的模拟和依赖注入

时间:2016-10-26 00:07:12

标签: c++ unit-testing dependency-injection mocking googlemock

我正在努力用googlemock和C ++中的依赖注入进行单元测试。模拟和依赖注入显着简化了代码测试,但它们严重依赖于虚拟方法。虽然其他语言中的类默认使用虚方法,但不是C ++的情况。我使用C ++创建了一个低开销的性能测量框架,所以只是让每个类继承一个接口(带有纯虚方法)都不是一个理想的选择。

具体来说,我在测试包含以下对象集合的类时遇到问题:

struct event_info { /* ... */ };

template<typename Event>
class event_manager {
public:
  event_manager(const std::vector<event_info>& events) {
    std::transform(begin(events), end(events),
        std::back_inserter(events_),
        [](const event_info& info) { return Event{info}; });
  }

  void read() {
    for (auto& e : events_)
      e.read();
  }

  // ...

private:
  std::vector<Event> events_;
  // ...
};

为了测试这门课,我可以做以下事情:

class mock_event {
public:
  MOCK_METHOD0(read, void());
};

TEST(event_manager, test) {
  event_manager<mock_event> manager;
  // ...
}

但这不会起作用,因为我无法设置模拟对象的期望,并且来自googlemock的模拟对象不可复制(因此,对std::transform的调用无法编译。)

要解决此问题,在测试时我可以使用指针(例如event_manager<mock_event*>)并将工厂传递给event_manager构造函数。但是,由于e.read()之类的调用(在测试时它应该是e->read()),这不会编译。

然后我可以使用类型特征来创建一个方法,如果给定引用只返回引用,并且如果给定指针,则取消引用指针(例如,dereference(e).read())。但是,这只是增加了大量的复杂性,它看起来不是一个好的解决方案(特别是如果需要进行测试包含对象集合的所有类)。

所以,我想知道是否有更好的解决方案,或者只是模拟和依赖注入不是非常适合C ++的技术。

1 个答案:

答案 0 :(得分:1)

我假设您在使用一个模拟标准用例的简单类进行了适当的性能测量,然后才决定您的应用程序无法容忍通过指针取消引用的开销来执行虚函数调用。

如果您阅读gmock文档,他们会有一个“高性能模拟”部分,其中显示如何在生产代码中使用模板以允许模拟非虚拟函数。

我认为代码(生产或测试)的第一条规则是保持代码尽可能简单,因此我不相信用模板更改生产代码只是为了能够测试它(尽管另一方面)我完全有利于使用TDD作为我的生产代码设计的批评和指导。)

因此,您的应用程序看起来需要另一个模拟框架,允许在链接时执行模拟,而不是运行时。

查看cpputest和cppumock(https://cpputest.github.io/),它能够模拟C独立函数和C ++非虚拟方法。

使用cpputest / cppumock支付的价格是它需要比gmock更多的样板,但它非常好。

相关问题