Better MOCKing?

Better MOCKing?

本文关键字:MOCKing Better      更新时间:2023-10-16

问题:

假设我有一个类System,里面有一些Component。为了正确测试系统,我需要在System类中保留一个指向Component的指针,并将Component设计为具有一个带有虚拟方法的接口IComponent。然后在测试中,我可以创建IComponent的mock并将其提供给系统。

但在某些情况下,我不希望采取这种做法。

还有其他使用模板并将组件指定为模板参数的技术,如下所示:

template <class Component>
class System
{
// ...
Component _component;
};

然后,在我的测试中,我可以创建一个系统,给它一个组件模型,就像这样:

System<MockComponent> theSystem;

MockComponent不一定要从IComponent继承,在我的方法中,我不想要IComponet,我只希望MockComponent有一些所需的方法,与Component中的方法相同。

但这种方法的问题是,我想指示MockComponent做什么,给它一些期望,并告诉它返回什么。在测试中,我无法访问MockComponent,因为它位于系统中。如果系统有一个组件的getter,那也没关系,但有时我不想在系统中有一个Component的getter。然后,在测试中,我需要创建另一个类MockSystem,并为它配备组件的getter(同时确保原始系统中的_component位于"protected"部分)。

template <Class Component>
MockSystem : public System
{
public:
Component GetComponent() { return _component; }
};

然后,在测试中我可以:

MockSystem<MockComponent> theSystem;
MockComponent mockComponent = theSystem.GetComponent();
EXPECT_CALL(mockComponnent, ...);

这种方法效果很好。

但是。。。我想知道是否有一种方法可以简化这一点。

如果我有一种在编译时从System类生成MockSystem类的机制呢?我的意思是,我想为所有模板参数获得带有getter的System类。

我知道模板元编程可以创造奇迹,但我不是TPM方面的专家。我读过一些例子,看到Boost::Hana在行动,现在想知道这是否可行。

这里有人听说过或见过这样的框架吗?或者还有其他方法吗?

您可以在mock类上定义包装器,以允许访问mock:

struct ComponentMock
{
MOCK_METHOD0(foo, int());
};
struct ComponentMockWrapper
{
static ComponentMock* mock;
int foo()
{
return mock->foo();
}
};
ComponentMock* ComponentMockWrapper::mock;

然后,在您的测试套件中:

class SystemTestSuite : public testing::Test
{
protected:
ComponentMock componentMock;
void SetUp() override
{
ComponentMockWrapper::mock = &componentMock;
}
System<ComponentMockWrapper> objectUnderTest;
};

测试如下:

TEST_F(SystemTestSuite1, shallFooComponent)
{
EXPECT_CALL(componentMock, foo());
objectUnderTest.foo();
}

但要注意这样的问题,比如-如果需要2个或多个Component该怎么办,或者如何跟踪可测试Component的寿命。。。


也许更好的方法是牺牲一点封装来支持可测试性,即获得对组件的受保护访问:

template <class Component>
class System
{
protected:
Component& getComponent(); // for UT only
};

并通过推广此功能允许UT中的公共访问:

template <class Component>
class SystemTestable : public System<Component>
{
public:
using System<Component>::getComponent;
};
class SystemTestSuite : public testing::Test
{
protected:
SystemTestable<ComponentMock> objectUnderTest;
};