我有一个类,它是一个外部类的子类,我对它没有任何控制权。外部类依赖于系统资源。例如
class MyClass : public ExternalBase // This class is from external framework and framework requires it to derive from this class.
{
int doSomePrivateThing(int );
public:
virtual int DoSomething(int );
virtual ~MyClass();
}
int MyClass::doSomePrivateThing(int )
{
// do some private task
}
int MyClass::DoSomething(int n)
{
// Do MyClass Specific task
int k = doSomePrivateThing(n);
return ExternalBase::DoSomething(k); // This function depends on external system resources.
// Probably try to communicate with remote server
// or attempt access Storage or Display device etc.
}
MyClass::~MyClass()
{}
如何打破MyClass的依赖关系,为MyClass::DoSomething()编写单元测试。使用组合代替继承不是一种选择,因为框架需要从这个基类派生类。
我正在使用C++和GoogleTest/Mock。但任何广义的解决方案都值得赞赏。
提前谢谢。
有两种方法。我称之为"稍微正确一点"的方式和"非常丑陋"的方式。
"更正确"的方式:
用一些可以部分模拟的附加层封装外部类函数。
class MyClass : public ExternalBase // This class is from external framework and framework requires it to derive from this class.
{
int doSomePrivateThing(int );
public:
virtual void BaseDoSomething(int) { return ExternalBase::DoSomething(v); }
virtual int DoSomething(int v);
virtual ~MyClass();
};
int MyClass::DoSomething(int n)
{
// Do MyClass Specific task
int k = doSomePrivateThing(n);
return BaseDoSomething(k);
}
以这种方式在UT中进行部分模拟:
class TestableMyClass : public MyClass
{
public:
using MyClass::MyClass;
MOCK_METHOD1(BaseDoSomething, int(int));
};
TEST(A,A)
{
TestableMyClass objectUnderTest;
EXPECT_CALL(objectUnderTest, BaseDoSomething(112));
objectUnderTest.DoSomething(112);
}
当您需要在测试中调用真正的基类方法时,请将WillOnce(Invoke...)
与EXPECT_call一起使用。
"非常丑陋"的方式:
提供您自己的ExternalBase的UnitTest实现,并将其链接到您的测试。ExternalBase的这种"UnitTest"实现应该基于一些全局Mocks对象。
ExternalBaseMock.hpp:
class ExternalBaseMock
{
public:
MOCK_METHOD1(DoSomething, int(int));
};
extern ExternalBaseMock externalBaseMock;
ExternalBaseMock.cpp:
ExternalBaseMock externalBaseMock;
int ExternalBase::DoSomething(int n)
{
return externalBaseMock.DoSomething(n);
}
然后你的测试:
#include "ExternalBaseMock.hpp"
TEST(A,A)
{
MyClass objectUnderTest;
EXPECT_CALL(externalBaseMock, DoSomething(112));
objectUnderTest.DoSomething(112);
}