1

我正在使用 Visual Studio 2013 单元测试。我的代码使用time()函数来生成一些对象名称,因此在测试时很难确保一致的行为。

如果是 C#,我可以使用 shims,如文章http://msdn.microsoft.com/en-us/library/hh549175.aspx “开始使用 shims”部分中所示。

有什么技术可以time()在我的 C++ 单元测试期间重新定义调用吗?

4

4 回答 4

3

您想要做的就是模拟(http://en.wikipedia.org/wiki/Mock_object)。

就我个人而言,我喜欢使用 Hippomocks ( https://github.com/dascandy/hippomocks ),它允许在当前版本中模拟 C 和 C++ 函数(除了非虚拟类方法的显着例外,参见例如Mocking non-virtual C++ 中的方法无需编辑生产代码?)。

于 2014-01-16T15:05:15.130 回答
3

你不能“重新定义”这些东西本身,但你可以提供一个不同名称的替代实现,并使区别在你的真实代码中几乎是透明的。

通常,您模拟函数

CxxTest采用让您调用T::time()而不是std::time()(足够接近!)的方法;使用宏/包含技巧,该调用将解析为std::time()您提供的任何替换实现。

这是一个非常简单的、非常幼稚的示例,仅用于演示基本原理:

foo.h

#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;
}

测试.cpp

#define USE_MOCKS
#include "foo.h"
#include <cassert>

int main()
{
   mock_time_result = 50;
   assert(foo() == 5);

   mock_time_result = 400;
   assert(foo() == 40);
}

模拟函数.h

#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
   }
}

模拟时间.h

#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
于 2014-01-16T15:18:29.113 回答
2

如果你不包括ctime,或者time.h你可以写任何你想要的定义time()

于 2014-01-16T14:38:09.260 回答
1

单元测试需要确定性代码。 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);
于 2014-01-16T15:29:27.160 回答