如何使用 googletest 捕获标准输出/标准错误?
Posted
技术标签:
【中文标题】如何使用 googletest 捕获标准输出/标准错误?【英文标题】:How to capture stdout/stderr with googletest? 【发布时间】:2011-04-17 17:41:19 【问题描述】:使用googletest框架时是否可以捕获stdout和stderr?
例如,我想调用一个将错误写入控制台(stderr)的函数。 现在,在测试中调用函数时,我想断言那里没有输出。
或者,也许我想测试错误行为并断言当我(故意)产生错误时会打印某个字符串。
【问题讨论】:
从设计的角度来看,我建议修改实现以减少切换到日志文件的痛苦。例如,使用ostream
接口会更容易。
【参考方案1】:
避免这样做总是一个好的设计理念。如果您真的想这样做,请执行以下操作:
#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。对于不通过文件进行捕获,您可以使用管道对代替。
【讨论】:
【参考方案2】:我之前使用过这个 sn-p 在测试输出时将 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);
在重定向回原始输出之前,使用您的谷歌测试检查缓冲区中的输出。
【讨论】:
这对 googletest 不起作用,因为 gtest 使用 printf 直接进入标准输出,绕过你的重定向。但如果你想截取cout << ...
的输出,这是一个很好的解决方案。我会创建一个辅助类来自动恢复析构函数中的原始streambuf ...
我添加了一种方法,可以将其与 Google 测试一起使用,除非出现上述情况。【参考方案3】:
Googletest 为此提供了功能:
testing::internal::CaptureStdout();
std::cout << "My test";
std::string output = testing::internal::GetCapturedStdout();
【讨论】:
可能是最简单的解决方案 testing::internal::CaptureStderr() 也存在。此处使用例如:googletest.googlecode.com/svn/trunk/test/… 应该知道这是一个私有 API,因此不受支持。我建议不要这样做 我发现如果多次调用,即使在不同的测试中,也会出现段错误,因此毫无价值。 @NirFriedman 似乎必须在每个捕获之前致电::testing::internal::CaptureStdout()
。【参考方案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);
...
【讨论】:
虽然这通常是一个好主意,但在他的情况下它不起作用,因为 gtest 是一个外部库(从他的角度来看),他想在不修改源的情况下捕获框架的输出代码。【参考方案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 的信息,请参阅此答案: https://***.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
【讨论】:
【参考方案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 测试夹具中,可以这样写:
namespace
class MyTestFixture : public ::testing::Test
protected:
MyTestFixture() : sbufnullptr
// 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, ***Test)
std::string expected"Hello";
// Use cout as usual
std::cout << expected;
std::string actualbuffer.str();
EXPECT_EQ(expected, actual);
// end namespace
【讨论】:
以上是关于如何使用 googletest 捕获标准输出/标准错误?的主要内容,如果未能解决你的问题,请参考以下文章