如何在生产中避免vtable查找,并且仍然能够在单元测试中进行广泛的模拟

How to avoid vtable-lookups in production, and still be able to mock extensively in unit-tests?

本文关键字:单元测试 模拟 生产中 vtable 查找      更新时间:2023-10-16

在C#这样的语言中,我经常让我的类依赖于接口而不是具体的类,即使只有一个"真正的"实现。这是为了使单元测试更容易(通过创建mock(。

当用C++编程时,我希望获得最大的性能,并且仍然使用TDD和mocking。有没有办法同时实现这两个目标?也就是说,能够在测试时进行模拟,并且在生产代码中仍然有直接的具体调用?

如何在C++中实现这一点?

因此,代替接口和运行时多态性:

struct IService
{
virtual ~IService() = 0;
virtual void foo() = 0;
// ...
};
class C
{
public:
explicit C(IService& service) : service(service) {}
void DoJob() { /*..*/ service.foo(); /*..*/ }
// ..
private:
IService& service;
};

生产中:

struct Service : IService
{
void foo() override;
// ...
};
Service service;
C c(service);
c.doJob();

测试中

struct ServiceMock : IService
{
void foo() override;
// ...
};
ServiceMock service;
C c(service);
c.doJob();

您可以使用模板:

// No interface to provide requirement, so document it
// TService must have `foo()`, ...
// or wait for C++20 concept
template <typename TService>
class C
{
public:
explicit C(TService& service) : service(service) {}
void DoJob() { /*..*/ service.foo(); /*..*/ }
// ..
private:
TService& service;
};

调用代码主要类似于运行时代码:-没有继承权。-不过在C++17之前更详细。:/

生产中:

struct Service
{
void foo();
// ...
};
Service service;
C c(service); // or before C++17 C<Service> c(service);
c.doJob();

测试中:

struct ServiceMock
{
void foo();
// ...
};
ServiceMock service;
C c(service); // or before C++17 C<ServiceMock> c(service);
c.doJob();

但这种模板与const一样具有传染性(调用可测试代码也应该是模板(。

在C++17之前,它似乎真的太冗长了:

OuterService<ServiceA<Service1, Service2>, ServiceB<Service1, Service2>> outer(serviceA, servceB);
// versus
// OuterService outer(serviceA, servceB);