C++ 如何使用googletest捕获stdout/stderr?

C++ 如何使用googletest捕获stdout/stderr?,c++,unit-testing,stdout,stderr,googletest,C++,Unit Testing,Stdout,Stderr,Googletest,使用框架时是否可以捕获stdout和stderr 例如,我想调用一个将错误写入控制台(stderr)的函数。 现在,当在测试中调用函数时,我想断言没有输出出现在那里 或者,也许我想测试错误行为,并断言当我(故意)产生错误时,会打印某个字符串。避免这样做始终是一个好的设计想法。如果您真的想这样做,请执行以下操作: #include <cstdio> #include <cassert> #include <sys/types.h> #include <sy

使用框架时是否可以捕获stdout和stderr

例如,我想调用一个将错误写入控制台(stderr)的函数。 现在,当在测试中调用函数时,我想断言没有输出出现在那里


或者,也许我想测试错误行为,并断言当我(故意)产生错误时,会打印某个字符串。

避免这样做始终是一个好的设计想法。如果您真的想这样做,请执行以下操作:

#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);
}
#包括
#包括
#包括
#包括
#包括
#包括
int main(){
int fd=open(“my_file.log”,O_WRONLY | O_create | O_TRUNC,0660);
断言(fd>=0);
int-ret=dup2(fd,1);
断言(ret>=0);
printf(“现在是标准输出!\n”);

std::cout在测试输出时,我曾经使用过这个片段将cout调用重定向到stringstream。希望它能激发一些想法。我以前从未使用过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);
//这也可以是一个流,也可以是任何其他ostream
std::stringstream缓冲区;
//在这里保存库特的缓冲区
std::streambuf*sbuf=std::cout.rdbuf();
//将cout重定向到我们的stringstream缓冲区或任何其他ostream
std::cout.rdbuf(buffer.rdbuf());
//照常使用cout

std::coutGoogletest提供了以下功能:

testing::internal::CaptureStdout();
std::cout << "My test";
std::string output = testing::internal::GetCapturedStdout();
testing::internal::CaptureStdout();

std::cout不要这样做,而是使用依赖项注入来删除直接使用
std::cout
。在测试代码中,使用类
std:ostringstream
的模拟对象作为模拟对象,而不是真正的
std::cout

因此,与此相反:

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

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

std::cout我们正在做您所指的事情

首先,我们创建了一些宏:

    #define CAPTURE_STDOUT StdoutRedirect::instance().redirect();
    #define RELEASE_STDOUT StdoutRedirect::instance().reset();
    #define ASSERT_INFO( COUNT, TARGET )   \
      ASSERT_PRED_FORMAT2(OurTestPredicates::AssertInfoMsgOutput, TARGET, COUNT );
有关捕获标准输出和标准输出,请参见以下答案: 只需使用它们的BeginCapture()、EndCapture()代替重定向()和重置()

在AssertInfo Msgoutput方法中:

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

基于wgafa的答案,我创建了这个helper类,它可以用
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;

};

将Wgaffa的建议(我喜欢)放到Google测试夹具中,您可能会写道:

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

名称空间{
类MyTestFixture:public::testing::Test{
受保护的:
MyTestFixture():sbuf{nullptr}{
//故意清空
}
~MyTestFixture()override=默认值;
//在每个单元测试之前调用
void SetUp()覆盖{
//保存库特的缓冲区。。。
sbuf=std::cout.rdbuf();
//将cout重定向到我们的stringstream缓冲区或任何其他ostream
std::cout.rdbuf(buffer.rdbuf());
}
//在每个单元测试之后调用
void TearDown()重写{
//完成后,它会回到原来的自己
标准::cout.rdbuf(sbuf);
sbuf=nullptr;
}
//以下对象可以在每个单元测试中重用
//这也可以是一个流或任何其他流
std::stringstream缓冲区{};
//在这里保存库特的缓冲区
标准::streambuf*sbuf;
};
测试F(MyTestFixture,StackOverflowTest){
std::字符串应为{“Hello”};
//照常使用cout

std::cout从设计的角度来看,我建议修改实现,这样切换到日志文件就不会那么痛苦了。例如,使用
ostream
接口会更容易。可能是最简单的解决方案testing::internal::CaptureStderr()也存在。在这里使用。例如:应该知道这是一个私有API,因此不受支持。我建议不要使用itI。如果多次调用它,即使在不同的测试中也会发现它是segfault,因此毫无价值。@Nirfiedman似乎必须调用
::testing::internal::CaptureStdout()
在每次捕获之前。这不适用于googletest,因为gtest使用直接进入标准输出的printf,绕过重定向。但是如果您想拦截
的输出,这是一个很好的解决方案。我可以添加一种方法,在Google Test中使用此方法,除非出现上面评论的场景。虽然这通常是一个好主意l、 在他的情况下,这是行不通的,因为gtest是一个外部库(从他的观点来看),他希望在不修改源代码的情况下捕获框架的输出。
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;

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