Как переопределить стандартные функции C ++ при тестировании в Visual Studio?

Я использую модульное тестирование Visual Studio 2013. Мой код использует time() функция для генерации некоторых имен объектов и, следовательно, трудно обеспечить согласованное поведение при тестировании.

Если бы это был C #, я мог бы использовать прокладки, как показано в статье http://msdn.microsoft.com/en-us/library/hh549175.aspx Раздел «Начало работы с прокладками».

Есть ли методика, как переопределить time() звонки во время моего модульного теста C ++?

0

Решение

То, что вы хотите сделать, называется насмешкой (http://en.wikipedia.org/wiki/Mock_object).

Лично мне нравится использовать Hippomocks (https://github.com/dascandy/hippomocks) который позволяет в текущей версии имитировать функции C и C ++ (с заметным исключением не-виртуальных методов класса, см., например, Пересмешивать не виртуальные методы в C ++ без редактирования рабочего кода?).

3

Другие решения

Если вы не включите ctime или же time.h тогда вы можете написать любое определение для time()

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
2

Модульное тестирование требует детерминированного кода. 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);
1
По вопросам рекламы [email protected]