Better MOCKing?
Better MOCKing?
问题:
假设我有一个类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;
};