Как захватить stdout/stderr с googletest?
Можно ли записать stdout и stderr при использовании googletest framework?
Например, я хотел бы вызвать функцию, которая записывает ошибки в консоль (stderr).
Теперь, когда вы вызываете функцию в тестах, я хочу утверждать, что там не появляется вывод.
Или, может быть, я хочу проверить поведение ошибки и хочу утверждать, что определенная строка печатается, когда я (намеренно) выдает ошибку.
Ответы
Ответ 1
Я использовал этот фрагмент раньше, чтобы перенаправлять вызовы cout на строковый поток при тестировании вывода. Надеюсь, это может вызвать некоторые идеи. Я никогда раньше не использовал googletest.
// This can be an ofstream as well or any other ostream
std::stringstream buffer;
// Save cout buffer here
std::streambuf *sbuf = std::cout.rdbuf();
// Redirect cout to our stringstream buffer or any other ostream
std::cout.rdbuf(buffer.rdbuf());
// Use cout as usual
std::cout << "Hello World";
// When done redirect cout to its old self
std::cout.rdbuf(sbuf);
Прежде чем перенаправлять обратно на исходный вывод, используйте свой тест google для проверки вывода в буфере.
Ответ 2
Googletest предлагает функции для этого:
testing::internal::CaptureStdout();
std::cout << "My test";
std::string output = testing::internal::GetCapturedStdout();
Ответ 3
Избегая необходимости делать это, всегда хорошая идея дизайна. Если вы действительно хотите сделать это, выполните следующие действия:
#include <cstdio>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
int main() {
int fd = open("my_file.log", O_WRONLY|O_CREAT|O_TRUNC, 0660);
assert(fd >= 0);
int ret = dup2(fd, 1);
assert(ret >= 0);
printf("This is stdout now!\n");
std::cout << "This is C++ iostream cout now!" << std::endl;
close(fd);
}
Чтобы использовать stderr вместо stdout, измените второй аргумент на dup2 равным 2. Для захвата, не проходя через файл, вы можете использовать пару труб.
Ответ 4
Вместо этого используйте инъекцию зависимостей, чтобы удалить прямое использование std::cout
. В вашем тестовом коде используйте макет объекта класса std:ostringstream
как макет объекта вместо реального std::cout
.
Итак, вместо этого:
void func() {
...
std::cout << "message";
...
}
int main (int argc, char **argv) {
...
func();
...
}
:
void func(std::ostream &out) {
...
out << "message";
...
}
int main(int argc, char **argv) {
...
func(std::cout);
...
}
Ответ 5
Мы делаем именно то, что вы имеете в виду.
Сначала мы создали несколько макросов:
#define CAPTURE_STDOUT StdoutRedirect::instance().redirect();
#define RELEASE_STDOUT StdoutRedirect::instance().reset();
#define ASSERT_INFO( COUNT, TARGET ) \
ASSERT_PRED_FORMAT2(OurTestPredicates::AssertInfoMsgOutput, TARGET, COUNT );
Смотрите этот ответ для захвата stdout и stderr: fooobar.com/questions/135381/... Просто используйте их BeginCapture(), EndCapture() вместо наших redirect() и reset().
В методе AssertInfoMsgOutput:
AssertionResult OurTestPredicates::AssertInfoMsgOutput( const char* TARGET,
const char* d1,
const char* d2,
int COUNT )
{
int count = 0;
bool match = false;
std::string StdOutMessagge = GetCapture();
// Here is where you process the stdout/stderr info for the TARGET, and for
// COUNT instances of that TARGET message, and set count and match
// appropriately
...
if (( count == COUNT ) && match )
{
return ::testing::AssertionSuccess();
}
return :: testing::AssertionFailure() << "not found";
}
Теперь в вашем модульном тесте просто оберните ваши вызовы, которые вы хотите захватить stdout/stderr:
CAPTURE_STDOUT
// Make your call to your code to test / capture here
ASSERT_INFO( 1, "Foo bar" );
RELEASE_STDOUT
Ответ 6
Основываясь на ответе Wgaffa, я создал этот вспомогательный класс, который можно создать с помощью std::cout
или std::cerr
:
class CaptureHelper
{
public:
CaptureHelper(std::ostream& ioStream)
: mStream(ioStream),
mIsCapturing(false)
{ }
~CaptureHelper()
{
release();
}
void capture()
{
if (!mIsCapturing)
{
mOriginalBuffer = mStream.rdbuf();
mStream.rdbuf(mRedirectStream.rdbuf());
mIsCapturing = true;
}
}
std::string release()
{
if (mIsCapturing)
{
std::string wOutput = mRedirectStream.str();
mStream.rdbuf(mOriginalBuffer);
mIsCapturing = false;
return wOutput;
}
}
private:
std::ostream& mStream;
bool mIsCapturing;
std::stringstream mRedirectStream;
std::streambuf* mOriginalBuffer;
};
Ответ 7
Поместив предложение Wgaffa (которое мне нравится) в прибор Google Test, можно написать:
namespace {
class MyTestFixture : public ::testing::Test {
protected:
MyTestFixture() : sbuf{nullptr} {
// intentionally empty
}
~MyTestFixture() override = default;
// Called before each unit test
void SetUp() override {
// Save cout buffer...
sbuf = std::cout.rdbuf();
// Redirect cout to our stringstream buffer or any other ostream
std::cout.rdbuf(buffer.rdbuf());
}
// Called after each unit test
void TearDown() override {
// When done redirect cout to its old self
std::cout.rdbuf(sbuf);
sbuf = nullptr;
}
// The following objects can be reused in each unit test
// This can be an ofstream as well or any other ostream
std::stringstream buffer{};
// Save cout buffer here
std::streambuf *sbuf;
};
TEST_F(MyTestFixture, QaruTest) {
std::string expected{"Hello"};
// Use cout as usual
std::cout << expected;
std::string actual{buffer.str()};
EXPECT_EQ(expected, actual);
}
} // end namespace