bet*_*ido 5 c++ unit-testing dependency-injection mocking googlemock
我正在努力用C++中的googlemock和依赖注入进行单元测试.模拟和依赖注入显着简化了代码测试,但它们严重依赖于虚拟方法.虽然其他语言中的类默认使用虚方法,但不是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_;
// ...
};
Run Code Online (Sandbox Code Playgroud)
为了测试这个课程,我可以做以下事情:
class mock_event {
public:
MOCK_METHOD0(read, void());
};
TEST(event_manager, test) {
event_manager<mock_event> manager;
// ...
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为我无法设置模拟对象的期望,并且来自googlemock的模拟对象不可复制(因此,调用std::transform无法编译).
要解决这个问题,在测试时我可以使用指针代替(例如event_manager<mock_event*>)并将工厂传递给event_manager构造函数.但是,由于诸如e.read()(e->read()在测试时应该改为)这样的调用,这将无法编译.
然后,我可以使用类型特征来创建一个方法,如果给定引用刚刚返回引用,并且如果给定指针,则取消引用指针(例如,dereference(e).read()).但是,这只是增加了大量的复杂性,它看起来不是一个好的解决方案(特别是如果需要进行测试包含对象集合的所有类).
所以,我想知道是否有更好的解决方案,或者只是模拟和依赖注入不是非常适合C++的技术.
我假设您在确定您的应用程序无法容忍通过指针取消引用来执行虚拟函数调用的开销之前,使用模拟标准用例的简单类进行了适当的性能测量。
如果您阅读 gmock 文档,他们有一个“高性能模拟”部分,其中展示了如何在生产代码中使用模板来模拟非虚拟函数。
我认为代码(生产或测试)的第一条规则是使代码尽可能简单,因此我不相信用模板更改生产代码只是为了能够测试它(尽管另一方面我完全赞成使用 TDD 作为对我的生产代码设计的批评和指导)。
因此,您的应用程序似乎需要另一个模拟框架,该框架允许在链接时而不是运行时执行模拟。
查看 cpputest 和 cppumock ( https://cpputest.github.io/ ),它们能够模拟 C 独立函数和 C++ 非虚拟方法。
使用 cpputest/cpumock 的代价是它比 gmock 需要更多的样板,但它非常好。