C++ 如何使用 googletest 捕获 stdout/stderr?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3803465/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to capture stdout/stderr with googletest?
提问by Jan Rüegg
Is it possible to capture the stdout and stderr when using the googletestframework?
使用googletest框架时是否可以捕获stdout和stderr ?
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.
例如,我想调用一个将错误写入控制台 (stderr) 的函数。现在,在测试中调用该函数时,我想断言那里没有输出。
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.
或者,也许我想测试错误行为并想断言在我(故意)产生错误时打印了某个字符串。
采纳答案by Wgaffa
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.
在测试输出时,我之前使用过此代码段将 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);
Before redirecting back to the original output use your google test to check the output in buffer.
在重定向回原始输出之前,使用您的 google 测试检查缓冲区中的输出。
回答by Heinzi
Googletest offers functions for this:
Googletest 为此提供了以下功能:
testing::internal::CaptureStdout();
std::cout << "My test";
std::string output = testing::internal::GetCapturedStdout();
回答by Flexo
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.
要使用 stderr 而不是 stdout 将 dup2 的第二个参数更改为 2。对于不通过文件进行捕获,您可以使用管道对。
回答by Raedwald
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
.
与其这样做,不如使用依赖注入来删除直接使用std::cout
. 在您的测试代码中,使用类std:ostringstream
的模拟对象作为模拟对象而不是真实的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);
...
}
回答by Tom D
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/9796918Just use their BeginCapture(), EndCapture() in place of our redirect() and reset().
请参阅此答案以获取 stdout 和 stderr:https: //stackoverflow.com/a/5419409/9796918 只需使用它们的 BeginCapture()、EndCapture() 代替我们的 redirect() 和 reset()。
In the AssertInfoMsgOutput method:
在 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";
}
Now in your unit test just wrap your calls that you want to capture stdout/stderr with:
现在在您的单元测试中,只需将您想要捕获 stdout/stderr 的调用包装起来:
CAPTURE_STDOUT
// Make your call to your code to test / capture here
ASSERT_INFO( 1, "Foo bar" );
RELEASE_STDOUT
回答by Kevin Pastor
Based on the answer of Wgaffa I made this helper class which can be constructed with either std::cout
or std::cerr
:
根据 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;
};
回答by Jim Daehn
Putting Wgaffa's suggestion (which I like) to a Google Test fixture, one might write:
将 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