我正在使用 Visual Studio 2013 单元测试。我的代码使用time()
函数来生成一些对象名称,因此在测试时很难确保一致的行为。
如果是 C#,我可以使用 shims,如文章http://msdn.microsoft.com/en-us/library/hh549175.aspx “开始使用 shims”部分中所示。
有什么技术可以time()
在我的 C++ 单元测试期间重新定义调用吗?
我正在使用 Visual Studio 2013 单元测试。我的代码使用time()
函数来生成一些对象名称,因此在测试时很难确保一致的行为。
如果是 C#,我可以使用 shims,如文章http://msdn.microsoft.com/en-us/library/hh549175.aspx “开始使用 shims”部分中所示。
有什么技术可以time()
在我的 C++ 单元测试期间重新定义调用吗?
您想要做的就是模拟(http://en.wikipedia.org/wiki/Mock_object)。
就我个人而言,我喜欢使用 Hippomocks ( https://github.com/dascandy/hippomocks ),它允许在当前版本中模拟 C 和 C++ 函数(除了非虚拟类方法的显着例外,参见例如Mocking non-virtual C++ 中的方法无需编辑生产代码?)。
你不能“重新定义”这些东西本身,但你可以提供一个不同名称的替代实现,并使区别在你的真实代码中几乎是透明的。
通常,您模拟函数。
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
如果你不包括ctime
,或者time.h
你可以写任何你想要的定义time()
单元测试需要确定性代码。 std::time()
,根据定义,是非确定性的。这为您提供了 2 个选项: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);