How to capture stdout/stderr with googletest?
Asked Answered
L

7

79

Is it possible to capture the stdout and stderr when using the googletest framework?

For example, I would like to call a function that writes errors to the console (stderr). Now, when calling the function in the tests, I want to assert that no output appears there.

Or, maybe I want to test the error behaviour and want to assert that a certain string gets printed when I (deliberately) produce an error.

Latea answered 27/9, 2010 at 11:46 Comment(1)
From a design point of view, I would suggest modifying the implementation so that switching to log files be less painful. Using the ostream interface would make it easier for example.Unyoke
I
43

I have used this snippet before to redirect cout calls to a stringstream when testing output. Hopefully it might spark some ideas. I've never used googletest before.

// 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);

Before redirecting back to the original output use your google test to check the output in buffer.

Invitation answered 28/10, 2010 at 14:23 Comment(2)
This doesn't work for googletest since gtest uses printf which goes straight to stdout, circumventing your redirect. But it is a nice solution if you want to intercept the output of cout << .... I would create a helper class though to automatically restore the original streambuf in the destructor...Cotopaxi
I've added a way one might use this with Google Test barring the scenario commented above.Eckblad
C
138

Googletest offers functions for this:

testing::internal::CaptureStdout();
std::cout << "My test";
std::string output = testing::internal::GetCapturedStdout();
Craggy answered 17/10, 2015 at 11:41 Comment(10)
probably the simplest possible solutionOutofdoors
This is only available for stdout, not stderr? There are death tests that capture stderr, but in many cases you may not be testing for process termination.Yokefellow
testing::internal::CaptureStderr() also exists. Is used here for example: googletest.googlecode.com/svn/trunk/test/…Craggy
the message is still printed on screen? how to suppress that?Finnougrian
One should know that this is a private API and is therefore not supported. I'd advise against itOllie
I found this to segfault if called more than once, even in different tests, and therefore worthless.Forwarder
This solution won't work if the unit-under-test throws an exception.Backwash
This doesn't work for me. I still get no printout on the screen or in the log.Limousine
@NirFriedman it seems that one has to call ::testing::internal::CaptureStdout() prior to each capture.Curnin
@NirFriedman true, but do you know why ::testing::internal::CaptureStdout() needs to be called prior to each capture?Kimon
I
43

I have used this snippet before to redirect cout calls to a stringstream when testing output. Hopefully it might spark some ideas. I've never used googletest before.

// 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);

Before redirecting back to the original output use your google test to check the output in buffer.

Invitation answered 28/10, 2010 at 14:23 Comment(2)
This doesn't work for googletest since gtest uses printf which goes straight to stdout, circumventing your redirect. But it is a nice solution if you want to intercept the output of cout << .... I would create a helper class though to automatically restore the original streambuf in the destructor...Cotopaxi
I've added a way one might use this with Google Test barring the scenario commented above.Eckblad
B
7

Avoiding having to do this is always a good design idea. If you really want to do it the following works:

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

To use stderr instead of stdout change the second argument to dup2 to be 2. For capturing without going via a file you could use a pipe pair instead.

Brozak answered 27/10, 2010 at 9:56 Comment(0)
S
4

Rather than do this, use dependency injection to remove the direct use of std::cout. In your test code use a mock object of class std:ostringstream as a mock object instead of the real std::cout.

So instead of this:

 void func() {
    ...
    std::cout << "message";
    ...
 }

 int main (int argc, char **argv) {
    ...
    func();
    ...
 }

have this:

 void func(std::ostream &out) {
    ...
    out << "message";
    ...
 }

 int main(int argc, char **argv) {
    ...
    func(std::cout);
    ...
 }
Seleucia answered 14/12, 2017 at 21:22 Comment(3)
While this is a good idea in general, it won't work in his case because gtest is an external library (from his viewpoint) and he wants to capture the output of the framework without modifying the source code.Cotopaxi
What if he/she has many functions that prints some error messages or logs? This won't work in that case as adding extra parameter just for capturing a stream is not a good idea. One thing he/she can do is to use global stream object and change/redirect it according to needs. std::cout, std::cerr and std::clog are such global objects. It's better to use std::cout and redirect it to another stream when needed.Elvaelvah
@KeshacSabu if there are many such functions, there are many design faults in the code to be fixed. Fixing them is a good idea.Seleucia
E
2

Putting Wgaffa's suggestion (which I like) to a Google Test fixture, one might write:

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's 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's buffer here
        std::streambuf *sbuf;
    };

    TEST_F(MyTestFixture, StackOverflowTest) {
        std::string expected{"Hello"};
        // Use cout as usual
        std::cout << expected;
        std::string actual{buffer.str()};
        EXPECT_EQ(expected, actual);
    }
} // end namespace

Eckblad answered 14/10, 2019 at 3:7 Comment(1)
Just use a default member initializer for sbuf and you won't need the constructor and destructor. std::streambuf *sbuf{}Lupus
C
0

Based on the answer of Wgaffa I made this helper class which can be constructed with either std::cout or 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;

};
Cohesion answered 23/7, 2019 at 15:47 Comment(0)
R
-1

We are doing exactly what you are referring to.

First we created some macros:

    #define CAPTURE_STDOUT StdoutRedirect::instance().redirect();
    #define RELEASE_STDOUT StdoutRedirect::instance().reset();
    #define ASSERT_INFO( COUNT, TARGET )   \
      ASSERT_PRED_FORMAT2(OurTestPredicates::AssertInfoMsgOutput, TARGET, COUNT );

See this answer for capturing stdout and stderr: https://mcmap.net/q/262955/-is-there-a-way-to-redirect-stdout-stderr-to-a-string Just use their BeginCapture(), EndCapture() in place of our redirect() and reset().

In the AssertInfoMsgOutput method:

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

Now in your unit test just wrap your calls that you want to capture stdout/stderr with:

    CAPTURE_STDOUT
    // Make your call to your code to test / capture here
    ASSERT_INFO( 1, "Foo bar" );
    RELEASE_STDOUT
Rialto answered 16/2, 2019 at 18:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.