web-dev-qa-db-ja.com

googletestでstdout / stderrをキャプチャする方法?

googletest フレームワークを使用しているときにstdoutとstderrをキャプチャすることは可能ですか?

たとえば、コンソールにエラーを書き込む関数(stderr)を呼び出します。ここで、テストで関数を呼び出すときに、出力が表示されないことを表明したいと思います。

または、エラーの動作をテストし、(意図的に)エラーが発生したときに特定の文字列が出力されることを表明したい場合があります。

46
Jan Rüegg

以前にこのスニペットを使用して、出力のテスト時にcout呼び出しを文字列ストリームにリダイレクトしました。うまくいけば、sparkいくつかのアイデアかもしれません。これまでに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);

元の出力にリダイレクトする前に、Googleテストを使用してバッファ内の出力を確認します。

30
Wgaffa

Googletestはこのための関数を提供します:

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

これを回避することは、常に優れた設計アイデアです。あなたが本当にそれをしたいのであれば、以下の作品:

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

Stdoutの代わりにstderrを使用するには、2番目の引数をdup2に2に変更します。ファイルを経由せずにキャプチャーするには、代わりにパイプペアを使用できます。

2
Flexo

これを行うのではなく、依存関係注入を使用して、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);
    ...
 }
1
Raedwald

私たちはあなたが言及していることを正確に行っています。

まず、いくつかのマクロを作成しました。

    #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://stackoverflow.com/a/5419409/9796918 redirect()とreset()の代わりに、BeginCapture()、EndCapture()を使用します。

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
0
Tom D

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;

};
0
Kevin Pastor

Wgaffaの提案(私が好き)をGoogle Testフィクスチャに当てはめると、次のようになります。

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
Jim Daehn