73 votes

Comment capturer stdout/stderr avec googletest ?

Est-il possible de capturer le stdout et le stderr lors de l'utilisation de la fonction googletest cadre ?

Par exemple, je voudrais appeler une fonction qui écrit les erreurs dans la console (stderr). Maintenant, lorsque j'appelle la fonction dans les tests, je veux m'assurer qu'aucune sortie n'y apparaît.

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

6 votes

Du point de vue de la conception, je suggérerais de modifier l'implémentation pour que le passage aux fichiers journaux soit moins pénible. En utilisant le ostream faciliterait les choses, par exemple.

125voto

Heinzi Points 1491

Googletest propose des fonctions pour cela :

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

0 votes

Probablement la solution la plus simple possible

0 votes

Cette option n'est disponible que pour stdout pas stderr ? Il y a des tests de mort qui capturent stderr mais dans de nombreux cas, il se peut que vous ne testiez pas la fin du processus.

7 votes

Testing::internal::CaptureStderr() existe aussi. Elle est utilisée ici par exemple : googletest.googlecode.com/svn/trunk/test/

41voto

Wgaffa Points 136

J'ai déjà utilisé ce snippet pour rediriger les appels cout vers un stringstream lors de tests de sortie. J'espère que cela pourra donner des idées. Je n'ai jamais utilisé googletest auparavant.

// 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 originale, utilisez votre test google pour vérifier la sortie dans le tampon.

1 votes

Cela ne fonctionne pas pour googletest puisque gtest utilise printf qui va directement à stdout, contournant votre redirection. Mais c'est une bonne solution si vous voulez intercepter la sortie de cout << ... . Je créerais cependant une classe d'aide pour restaurer automatiquement le streambuf original dans le destructeur...

0 votes

J'ai ajouté une façon de l'utiliser avec Google Test, sauf dans le cas du scénario commenté ci-dessus.

8voto

Flexo Points 39273

Éviter d'avoir à le faire est toujours une bonne idée de conception. Si vous voulez vraiment le faire, la méthode suivante fonctionne :

#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, changez le second argument de dup2 en 2. Pour capturer sans passer par un fichier, vous pouvez utiliser une paire de tubes à la place.

4voto

Raedwald Points 8862

Plutôt que de procéder ainsi, utilisez l'injection de dépendances pour supprimer l'utilisation directe de l'option std::cout . Dans votre code de test, utilisez un objet fantaisie de la classe std:ostringstream comme un objet fantaisie au lieu de l'objet réel std::cout .

Donc, au lieu de ça :

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

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

avoir ça :

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

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

0 votes

Bien que ce soit une bonne idée en général, cela ne fonctionnera pas dans son cas car gtest est une bibliothèque externe (de son point de vue) et il veut capturer la sortie du framework sans modifier le code source.

2voto

Jim Daehn Points 1

En mettant la suggestion de Wgaffa (que j'aime bien) dans un dispositif de test Google, on pourrait écrire :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X