在Visual Studio中进行测试时如何覆盖标准C ++函数?

时间:2014-01-16 14:35:57

标签: c++ visual-studio unit-testing mocking shim

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

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

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

4 个答案:

答案 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)

如果您不包含ctimetime.h,那么您可以为time()

撰写您想要的任何定义

答案 2 :(得分:2)

您无法“重新定义”本身这些内容,但您可以使用其他名称提供替代实现,并在您的真实中区分几乎透明代码。

通常,您模拟功能

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

的main.cpp

#include "foo.h"
#include <iostream>

int main()
{
    std::cout << foo() << std::endl;
}

TEST.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);
}

嘲笑-functions.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
   }
}

模拟time.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

答案 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);