web-dev-qa-db-fra.com

Comment capturer stdout/stderr avec googletest?

Est-il possible de capturer le stdout et le stderr en utilisant le googletest framework?

Par exemple, je voudrais appeler une fonction qui écrit des erreurs sur la console (stderr) . Maintenant, lors de l'appel de la fonction dans les tests, je veux affirmer qu'aucune sortie n'y apparaît.

Ou peut-être que je veux tester le comportement d'erreur et affirmer qu'une certaine chaîne est imprimée lorsque je produis (délibérément) une erreur.

37
Jan Rüegg

J'ai déjà utilisé cet extrait avant de rediriger des appels cout vers un stringstream lors du test de la sortie. J'espère que cela pourrait susciter quelques idées. Je n'ai jamais utilisé googletest avant.

// 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);

Avant de rediriger vers la sortie d'origine, utilisez votre test Google pour vérifier la sortie dans la mémoire tampon.

28
Wgaffa

Googletest propose des fonctions pour cela:

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

Éviter de faire cela est toujours une bonne idée de design. Si vous voulez vraiment le faire, les travaux suivants:

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

Pour utiliser stderr au lieu de stdout, remplacez le deuxième argument par dup2 par 2. Si vous souhaitez capturer sans passer par un fichier, vous pouvez utiliser une paire de canaux.

2
Flexo

Plutôt que de faire cela, utilisez l'injection de dépendance pour supprimer l'utilisation directe de std::cout. Dans votre code de test, utilisez un objet fictif de classe std:ostringstream comme objet fictif au lieu du std::cout réel.

Donc au lieu de cela:

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

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

avoir ceci:

 void func(std::ostream &out) {
    ...
    out << "message";
    ...
 }

 int main(int argc, char **argv) {
    ...
    func(std::cout);
    ...
 }
1
Raedwald

Nous faisons exactement ce dont vous parlez.

Nous avons d'abord créé des 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 );

Voir cette réponse pour capturer stdout et stderr: https://stackoverflow.com/a/5419409/9796918 Utilisez simplement leur BeginCapture (), EndCapture () à la place de notre redirect () et réinitialiser().

Dans la méthode 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";
    }

Maintenant, dans votre test unitaire, enroulez simplement les appels que vous voulez capturer avec:

    CAPTURE_STDOUT
    // Make your call to your code to test / capture here
    ASSERT_INFO( 1, "Foo bar" );
    RELEASE_STDOUT
0
Tom D