How to capture stdout/stderr with googletest?

后端 未结 7 2067
心在旅途
心在旅途 2020-12-12 20:18

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 (

相关标签:
7条回答
  • 2020-12-12 21:02

    Googletest offers functions for this:

    testing::internal::CaptureStdout();
    std::cout << "My test";
    std::string output = testing::internal::GetCapturedStdout();
    
    0 讨论(0)
  • 2020-12-12 21:07

    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);
        ...
     }
    
    0 讨论(0)
  • 2020-12-12 21:15

    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;
    
    };
    
    0 讨论(0)
  • 2020-12-12 21:15

    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
    
    
    0 讨论(0)
  • 2020-12-12 21:16

    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.

    0 讨论(0)
  • 2020-12-12 21:17

    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://stackoverflow.com/a/5419409/9796918 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
    
    0 讨论(0)
提交回复
热议问题