Мне нужно написать тесты (используя google testing framework) для небольшой учебной программы, написанной не мной. (это просто небольшая консольная игра, которая может получать режимы из командной строки или просто получать их во время выполнения)
Есть проблема: я не могу изменить код souce, но почти во всех методах используются cout и cin. и мой вопрос «как ответить на запросы (cin) программы во время тестирования (что-то вроде получения данных для cin из строки)?».
Предполагая, что вы можете контролировать main()
(или другую функцию, вызываемую перед проверяемыми функциями), вы можете изменить std::cin
читает откуда и где std::cout
пишет:
int main(int ac, char* av[]) {
std::streambuf* orig = std::cin.rdbuf();
std::istringstream input("whatever");
std::cin.rdbuf(input.rdbuf());
// tests go here
std::cin.rdbuf(orig);
}
(аналогично для std::cout
)
В этом примере сохраняется оригинальный буфер потока std::cin
так что его можно заменить перед отъездом main()
, Затем он устанавливает std::cin
читать из потока строк. Это может быть и любой другой потоковый буфер.
Насколько я понимаю, вам нужно выполнить следующее:
Стандартный язык C ++ не имеет стандартных средств связи с другими программами. Вам понадобится помощь операционной системы (которую вы не указали).
С помощью только C ++ или же без особых вызовов ОС, Я предлагаю:
В противном случае, найдите в своей ОС OS API, чтобы узнать, как записывать драйверы перенаправления ввода / вывода.
Я знаю, что вы сказали, что не можете изменить код, но я отвечу на это так, как если бы вы могли. Реальный мир обычно позволяет (маленьким) модификациям приспосабливать тестирование.
Один из способов — обернуть ваши вызовы, которые требуют внешних входов (DB, пользовательский ввод, сокеты и т. Д.), В вызовы функций, которые являются виртуальными, чтобы вы могли их смоделировать. (Пример ниже). Но сначала книга рекомендаций по тестированию. Эффективная работа с устаревшим кодом это отличная книга для тестирования методов, которые не ограничиваются только унаследованным кодом.
class Foo {
public:
bool DoesSomething()
{
string usersInput;
cin >> usersInput;
if (usersInput == "foo") { return true; }
else { return false; }
}
};
Превратился бы в:
class Foo
{
public:
bool DoesSomething() {
string usersInput = getUserInput();
if (usersInput == "foo") { return true; }
else { return false; }
}
protected:
virtual std::string getUserInput() {
string usersInput;
cin >> usersInput;
return usersInput;
}
};
class MockFoo : public Foo {
public:
void setUserInput(std::string input) { m_input = input }
std::string getUserInput() {
return m_input;
}
};
TEST(TestUsersInput)
{
MockFoo foo;
foo.setUserInput("SomeInput");
CHECK_EQUAL(false, foo.DoesSomething());
foo.setUserInput("foo");
CHECK_EQUAL(true, foo.DoesSomething());
}
Вы можете улучшить тестируемость ваших классов, не используя cin
а также cout
непосредственно. Вместо этого используйте istream&
а также ostream&
передать входной источник и выходной приемник в качестве параметров. Это случай внедрения зависимости. Если вы сделаете это, вы можете передать в std::stringstream
вместо cin
, так что вы можете предоставить указанный ввод и получить на выходе из вашей тестовой среды.
Тем не менее, вы можете достичь аналогичного эффекта, превратив Cin и Cout в stringstream
с (по крайней мере временно). Для этого установите std :: stringbuf (или «позаимствуйте» один из std::stringstream
) и использовать cin.rdbuf(my_stringbuf_ptr)
изменить streambuf
использован cin
, Вы можете отменить это изменение в тестовом демонтаже. Для этого вы можете использовать такой код:
stringbuf test_input("One line of input with no newline", ios_base::in);
stringbuf test_output(ios_base::out);
streambuf * const cin_buf = cin.rdbuf(&test_input);
streambuf * const cout_buf = cout.rdbuf(&test_output);
test_func(); // uses cin and cout
cout.rdbuf(cout_buf);
cin.rdbuf(cin_buf);
string test_output_text = test_output.str();