Как захватить стандартный вывод / stderr с помощью googletest? - PullRequest
40 голосов
/ 27 сентября 2010

Можно ли захватить stdout и stderr при использовании googletest framework?

Например, я хотел бы вызвать функцию, которая записывает ошибки в консоль (stderr). Теперь, при вызове функции в тестах, я хочу утверждать, что там ничего не выводится.

Или, может быть, я хочу проверить поведение ошибки и утверждать, что определенная строка печатается, когда я (намеренно) выдаю ошибку.

Ответы [ 5 ]

66 голосов
/ 17 октября 2015

Googletest предлагает функции для этого:

testing::internal::CaptureStdout();
std::cout << "My test";
std::string output = testing::internal::GetCapturedStdout();
30 голосов
/ 28 октября 2010

Я использовал этот фрагмент раньше, чтобы перенаправлять вызовы cout в поток строк при тестировании вывода.Надеюсь, это может зажечь некоторые идеи.Я никогда раньше не использовал googletest.

// This can be an ofstream as well or any other ostream
std::stringstream buffer;

// Save cout's 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 голосов
/ 27 октября 2010

Избежать необходимости делать это всегда хорошая идея дизайна.Если вы действительно хотите сделать это, выполните следующие действия:

#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. Для захвата без прохождения через файл вы могли бы использовать пару каналов.

1 голос
/ 15 декабря 2017

Вместо того, чтобы сделать это, используйте внедрение зависимостей, чтобы удалить прямое использование 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);
    ...
 }
0 голосов
/ 16 февраля 2019

Мы делаем именно то, что вы имеете в виду.

Сначала мы создали несколько макросов:

    #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: https://stackoverflow.com/a/5419409/9796918 Просто используйте их 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
...