Zak*_*Zak 5 c++ unit-testing interface static-classes googlemock
我正在使用Google Mock,并且正在努力模拟C ++系统调用(特别是C ++ 11计时函数)。
我知道我应该创建一个接口,创建一个类以为我的实际实现实现该接口,然后在测试中模拟出该接口。我正在尝试编写一个嵌入式应用程序,所以这种间接听起来对我来说太昂贵了。
将系统调用并入Google Mock的最有效/最有效的方法是什么?
不,您不必求助于模拟静态类-这是许多选择之一。
如果您处于虚拟分配过多的嵌入式环境中,或者该体系结构的编译器/链接器优化器确实做得很糟糕,则可以尝试以下三种方式来模拟平台调用。
为简单起见,假设您要模拟std::this_thread名称空间中的函数,例如sleep_for(std::milliseconds)。
示例0-无法测试的基准
无需模拟,我们假设您的代码如下所示:
class untestable_class
{
public:
void some_function()
{
if (must_sleep())
{
auto sleep_duration = std::chrono::milliseconds(1000);
std::this_thread::sleep_for(sleep_duration);
}
}
};
Run Code Online (Sandbox Code Playgroud)
您将像这样使用该类:
void use_untestable_class()
{
untestable_class instance;
instance.some_function();
}
Run Code Online (Sandbox Code Playgroud)
由于依赖于标准库sleep_for函数,因此您具有平台依赖关系,some_function如果不实际进行集成测试,就很难进行单元测试。
示例1-可使用静态策略进行测试
通过使用类模板告诉我们的类使用特定的线程策略,我们可以抽象出单元测试中的平台依赖性。该策略可以是静态的,也可以是实例的-它们都消除了运行时对虚拟调度的需要,并且它们对于编译器/链接器的优化非常容易。
在静态策略的情况下,我们有一个取决于平台的“实际”策略:
struct system_thread_policy1
{
static void sleep_milliseconds(long milliseconds)
{
auto sleep_duration = std::chrono::milliseconds(milliseconds);
std::this_thread::sleep_for(sleep_duration);
}
};
Run Code Online (Sandbox Code Playgroud)
我们还有一个可以在单元测试中控制的“模拟”策略:
struct mock_thread_policy1
{
// Mock attributes to verify interactions.
static size_t sleep_milliseconds_count;
static size_t sleep_milliseconds_arg1;
// Resets the mock attributes before usage.
static void sleep_milliseconds_reset()
{
sleep_milliseconds_count = 0;
sleep_milliseconds_arg1 = 0;
}
static void sleep_milliseconds(size_t milliseconds)
{
sleep_milliseconds_count++;
sleep_milliseconds_arg1 = milliseconds;
}
};
// This is needed with MS compilers to keep all mock code in a header file.
__declspec(selectany) size_t mock_thread_policy1::sleep_milliseconds_count;
__declspec(selectany) size_t mock_thread_policy1::sleep_milliseconds_arg1;
Run Code Online (Sandbox Code Playgroud)
使用该策略的生产类将策略类型作为模板参数,并对其进行sleep_milliseconds静态调用:
template <typename thread_policy>
class testable_class1
{
public:
void some_function()
{
if (must_sleep())
{
thread_policy::sleep_milliseconds(sleep_duration_milliseconds);
}
}
private:
enum { sleep_duration_milliseconds = 1000 };
};
Run Code Online (Sandbox Code Playgroud)
在生产代码中,testable_class1使用“真实”策略实例化:
void use_testable_class1()
{
testable_class1<system_thread_policy1> instance;
instance.some_function();
}
Run Code Online (Sandbox Code Playgroud)
在单元测试中,testable_class1使用“模拟”策略实例化:
void test_testable_class1()
{
mock_thread_policy1::sleep_milliseconds_reset();
testable_class1<mock_thread_policy1> instance;
instance.some_function();
assert(mock_thread_policy1::sleep_milliseconds_count == 1);
assert(mock_thread_policy1::sleep_milliseconds_arg1 == 1000);
//assert("some observable behavior on instance");
}
Run Code Online (Sandbox Code Playgroud)
这种方法的优点:
sleep_for。这种方法的缺点:
示例2-可使用实例策略进行测试
在实例策略的情况下,我们有一个取决于平台的“实际”策略:
struct system_thread_policy2
{
void sleep_milliseconds(size_t milliseconds) const
{
auto sleep_duration = std::chrono::milliseconds(milliseconds);
std::this_thread::sleep_for(sleep_duration);
}
};
Run Code Online (Sandbox Code Playgroud)
我们还有一个可以在单元测试中控制的“模拟”策略:
struct mock_thread_policy2
{
mutable size_t sleep_milliseconds_count;
mutable size_t sleep_milliseconds_arg1;
mock_thread_policy2()
: sleep_milliseconds_count(0)
, sleep_milliseconds_arg1(0)
{
}
void sleep_milliseconds(size_t milliseconds) const
{
sleep_milliseconds_count++;
sleep_milliseconds_arg1 = milliseconds;
}
};
Run Code Online (Sandbox Code Playgroud)
使用该策略的生产类将策略类型作为模板参数,获取注入到构造器中的策略实例,并调用其实例sleep_milliseconds:
template <typename thread_policy>
class testable_class2
{
public:
testable_class2(const thread_policy& policy = thread_policy()) : m_thread_policy(policy) { }
void some_function() const
{
if (must_sleep())
{
m_thread_policy.sleep_milliseconds(sleep_duration_milliseconds);
}
}
private:
// Needed since the thread policy is taken as a reference.
testable_class2(const testable_class2&);
testable_class2& operator=(const testable_class2&);
enum { sleep_duration_milliseconds = 1000 };
const thread_policy& m_thread_policy;
};
Run Code Online (Sandbox Code Playgroud)
在生产代码中,testable_class2使用“真实”策略实例化:
void use_testable_class2()
{
const testable_class2<system_thread_policy2> instance;
instance.some_function();
}
Run Code Online (Sandbox Code Playgroud)
在单元测试中,testable_class2使用“模拟”策略实例化:
void test_testable_class2()
{
mock_thread_policy2 thread_policy;
const testable_class2<mock_thread_policy2> instance(thread_policy);
instance.some_function();
assert(thread_policy.sleep_milliseconds_count == 1);
assert(thread_policy.sleep_milliseconds_arg1 == 1000);
//assert("some observable behavior on instance");
}
Run Code Online (Sandbox Code Playgroud)
这种方法的优点:
sleep_for。
这种方法的缺点:
testable_class2)增加噪音-如果不需要验证交互,则可以按构造函数中的值传递策略,并且大多数类goo都会消失。示例3-可使用虚拟策略进行测试
这与前两个示例不同,后者依赖于虚拟调度,但是如果编译器/链接器可以检测到所操作的实例是基本类型,则可能使编译器/链接器优化虚拟调度。
首先,我们有一个生产基类,它在非纯虚函数中使用“真实”策略:
class testable_class3
{
public:
void some_function()
{
if (must_sleep())
{
sleep_milliseconds(sleep_duration_milliseconds);
}
}
private:
virtual void sleep_milliseconds(size_t milliseconds)
{
auto sleep_duration = std::chrono::milliseconds(milliseconds);
std::this_thread::sleep_for(sleep_duration);
}
enum { sleep_duration_milliseconds = 1000 };
};
Run Code Online (Sandbox Code Playgroud)
其次,我们有一个派生类,它在虚函数(一种模板方法设计模式)中实现“模拟”策略:
class mock_testable_class3 : public testable_class3
{
public:
size_t sleep_milliseconds_count;
size_t sleep_milliseconds_arg1;
mock_testable_class3()
: sleep_milliseconds_count(0)
, sleep_milliseconds_arg1(0)
{
}
private:
virtual void sleep_milliseconds(size_t milliseconds)
{
sleep_milliseconds_count++;
sleep_milliseconds_arg1 = milliseconds;
}
};
Run Code Online (Sandbox Code Playgroud)
在生产代码中,testable_class3仅被实例化为自身:
void use_testable_class3()
{
// Lots of opportunities to optimize away the virtual dispatch.
testable_class3 instance;
instance.some_function();
}
Run Code Online (Sandbox Code Playgroud)
在单元测试中,testable_class3使用“模拟”派生类实例化:
void test_testable_class3()
{
mock_testable_class3 mock_instance;
auto test_function = [](testable_class3& instance) { instance.some_function(); };
test_function(mock_instance);
assert(mock_instance.sleep_milliseconds_count == 1);
assert(mock_instance.sleep_milliseconds_arg1 == 1000);
//assert("some observable behavior on mock_instance");
}
Run Code Online (Sandbox Code Playgroud)
这种方法的优点:
sleep_for。这种方法的缺点:
final(C ++ 11),因为必须允许从该基类继承该基类,并且如果比上述简单示例复杂得多,这可能会影响该类的其余部分设计。测试运行
可以使用以下方法测试以上所有内容:
int _tmain(int argc, _TCHAR* argv[])
{
test_testable_class1();
test_testable_class2();
test_testable_class3();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
完整的可运行示例在http://pastebin.com/0qJaQVcD