我正在使用Visual Studio 2013单元测试。我的代码使用time()
函数生成一些对象名称,因此在测试时很难确保一致的行为。
如果是C#,我可以使用垫片,如文章http://msdn.microsoft.com/en-us/library/hh549175.aspx“垫片入门”部分所示。
有没有什么技术可以在我的C ++单元测试中重新定义time()
个调用?
答案 0 :(得分:3)
你想做的事情叫做模拟(http://en.wikipedia.org/wiki/Mock_object)。
我个人喜欢使用Hippomocks(https://github.com/dascandy/hippomocks),它允许在当前版本中模拟C和C ++函数(非虚拟类方法的明显例外,参见例如Mocking non-virtual methods in C++ without editing production code?)。
答案 1 :(得分:2)
如果您不包含ctime
或time.h
,那么您可以为time()
答案 2 :(得分:2)
您无法“重新定义”本身这些内容,但您可以使用其他名称提供替代实现,并在您的真实中区分几乎透明代码。
通常,您模拟功能。
CxxTest采用让您调用T::time()
而不是std::time()
(足够接近!)的方法;使用宏/包含技巧,该调用将解析为std::time()
或您提供的任何替换实现。
这是一个非常简单,非常天真的例子,旨在展示基本原则:
#include "mocked-functions.h"
/**
* Takes the current time as a UNIX timestamp, and divides it by 10.
*/
inline int foo()
{
return T::time(NULL) / 10;
}
#include "foo.h"
#include <iostream>
int main()
{
std::cout << foo() << std::endl;
}
#define USE_MOCKS
#include "foo.h"
#include <cassert>
int main()
{
mock_time_result = 50;
assert(foo() == 5);
mock_time_result = 400;
assert(foo() == 40);
}
#include <ctime>
#ifdef USE_MOCKS
#include "mock-time.h"
#endif
namespace T {
time_t time(time_t* ptr)
{
#ifdef USE_MOCKS
return Mock_time(ptr);
#else
return std::time(ptr);
#endif
}
}
#include <ctime>
time_t mock_time_result = 0;
time_t Mock_time(time_t* ptr)
{
if (ptr) *ptr = mock_time_result
return mock_time_result;
}
$ g++ main.cpp -I. -o main
$ g++ test.cpp -I. -o test
$ ./main # output is the real time div by 10
$ ./test # output is nothing; all assertions succeed
答案 3 :(得分:1)
单元测试需要确定性代码。根据定义,std::time()
是非确定性的。这为您提供了两个选项:1)更改您将名称生成为确定性的方式,或2)模拟std::time()
。
似乎你的重点是#2,所以最简单的方法之一就是将std::time()
的调用包装在你自己的函数后面,以便生成名称。
std::string generate_name(bool use_time = true)
{
...
if (use_time)
{
// do something with time()
}
else
{
// return a value that is deterministic
}
}
您的单元测试将传递false
use_time
参数。
您可以避免在单元测试代码中包含<ctime>
,并编写自己的time()
版本来调用单元测试(这实际上更接近大多数TDD开发人员更喜欢的方法)。
由于您没有测试time
的功能,而是如何处理其输出,更好的方法(第一个选项)是将time
的输出传递给您的函数用来生成名字。这样,您的单元测试可以完全控制输入并测试输出。
std::string generate_name(unsigned long long id)
{
// do something to generate the name for the id
}
...
// production code
std::string name = generate_name(static_cast<unsigned long long>(std::time(NULL)));
// your unit test code could look like this
std::string name = generate_name(1ULL);