1
votes

Vous avez une entrée utilisateur depuis une boucle for dans un test JUnit, maintenant comment tester réellement sa sortie?

Supposons donc que j'ai la classe suivante avec methodA ():

class junitTests {
private final ByteArrayOutputStream output = new ByteArrayOutputStream();

@Before
public void setUpStreams() {
    System.setOut(new PrintStream(output));
}
@Test
void testA() {
    example ex = new example();
    ByteArrayInputStream in1 = new ByteArrayInputStream("13\n13\n13\n13\n13\n13\n".getBytes());

    System.setIn(in1);
    ex.methodA();
    String out = output.toString(); 

    System.out.println(out); //This isn't in the final unit test implementation, 
                            //just for demonstrating my issue.
}
}

Et j'ai un test JUnit capable de simuler l'entrée:

public class example {

public void methodA() {
    System.out.println("Enter in an integer for each step.\n");
    Scanner scan = new Scanner(System.in); 
    int currentStepCount = 0; 
    int totalStepCount = 0;

    for (int step = 10; step <= 16; step++) {
        if (step == 15)
            continue; // Skip fifteenth step

        System.out.println("\nStep " + step + ": ");
        currentStepCount = scan.nextInt();

        totalStepCount += currentStepCount;
    }

    System.out.println("\n\nAll steps have been taken in.\n");
    System.out.println("Total step count comes out to: " + totalStepCount + "\n"); 
    System.out.println("The last step count was " + currentStepCount + ". \n");
}
}


0 commentaires

3 Réponses :


1
votes

Puisqu'il s'agit d'un test JUnit, vous devriez tester la sortie en faisant une déclaration à ce sujet; par exemple

assertTrue(output.contains("Total step count comes out to: 15"));

si 15 est le résultat correct. Cependant, c'est une mauvaise façon d'écrire un test, car s'il échoue, vous ne savez pas quel était le nombre réel de pas (au lieu de 15).

Mieux vaut écrire votre méthode d'une manière qui renvoie le nombre de pas (ou ce que vous voulez tester), puis utilisez assertEquals pour faire l'assertion sur sa valeur. De cette façon, vous n'aurez pas besoin de rediriger System.out pour tester les résultats. Mieux encore, écrivez votre méthode pour prendre les nombres d'un tableau afin que vous n'ayez pas besoin de rediriger System.in non plus.

Cela dit, si vous voulez juste voir la sortie imprimée de la méthode, alors pourquoi la peine de rediriger System.out en premier lieu? Vous pouvez simplement laisser la méthode imprimer ce qu'elle imprime.


9 commentaires

Pour cette assertTrue, quand je l'utilise, elle devient false même si la sortie de la console indique "Le nombre total de pas sort à: 78".


Vous pouvez les combiner avec & , mais il vaut mieux faire des affirmations séparées, car alors au moins vous savez laquelle a échoué, même si vous ne savez pas comment elle a échoué.


Quant à 15 contre 78, je viens d'utiliser 15 (et j'ai dit "si 15 est le résultat correct") parce que je n'avais aucune idée du nombre auquel vous vous attendiez réellement. Vous devriez écrire 78 dans votre assertion si c'est le résultat correct.


Ouais 78 était ce à quoi je m'attendais, mais il est toujours faux. Quand j'essaye cette instruction assertTrue avec une chaîne vide "" , c'est la seule fois qu'elle est vraie. Même si je mets quelque chose comme "Step" là-dedans, c'est toujours faux.


Vous devrez peut-être faire out.flush (); avant out.toString () - cela change-t-il quelque chose?


Cela ne semblait rien changer non plus. Je pense que la string out = output.toString () est vide à chaque fois.


Oh, vous devez appeler flush () sur le PrintStream , je pense. Vous aurez besoin d'une variable distincte pour contenir une référence à cela.


J'ai ajouté une nouvelle variable appelée PrintStream ps = new PrintStream (System.out); puis juste après j'ai appelé ps.flush () mais cela n'a pas semblé changer quoi que ce soit non plus.


Non, vous devez vider le PrintStream que vous utilisez dans l'appel System.setOut (...) .



1
votes

Le dernier System.out.println utilise toujours votre PrintStream remplacé, donc votre tableau d'octets de sortie contient deux copies. Vous pouvez stocker le System.out d'origine et l'utiliser pour l'imprimer sur la console.

    private final ByteArrayOutputStream output = new ByteArrayOutputStream();
    private final PrintStream stdout = System.out;
    private final InputStream stdin = System.in;

    @Before
    public void setUpStreams() {
        System.setOut(new PrintStream(output));
    }

    @After
    public void restoreStreams() {
        System.setIn(stdin);
        System.setOut(stdout);
    }

    @Test
    public void testA() {
        example ex = new example();
        ByteArrayInputStream in1 = new ByteArrayInputStream("13\n13\n13\n13\n13\n13\n".getBytes());
        System.setIn(in1);
        ex.methodA();
        String out = output.toString();
        stdout.println(out);
    }


9 commentaires

Merci pour la réponse, mais cela me donne une NullPointerException à la ligne stdout.println (out) . Quand je le commente, cela fonctionne bien autrement.


Avez-vous mis stdout = System.out dans setUpStreams () ?


Ouais. J'ai fait à peu près mot pour mot ce que vous avez fait. Cela fonctionne-t-il quand vous le faites?


Oui, ça marche pour moi. Pouvez-vous vérifier si System.out n'est pas nul à ce stade? Et que vous exécutez en tant que test unitaire pour que @Before soit appelé avant le test?


J'ai modifié le code pour initialiser stdout / stdin lors de l'instanciation - cela peut fonctionner mieux pour vous


Je l'ai donc édité pour qu'il vérifie if (stdout! = Null) et si c'est faux, alors il imprime stdout.println (out); et cette fois ce n'est pas le cas semblent me donner une exception. Donc je pense que c'est nul. Et pour répondre à votre question, je clique avec le bouton droit sur le fichier de test junit et je l'exécute comme test junit.


Ok donc avec votre code mis à jour, il a bien fonctionné mais rien n'est imprimé après l'entrée. J'ai donc essayé de le mettre juste "Salut" et cela semblait bien imprimer. Donc, ma String out = output.toString () est une chaîne vide pour une raison quelconque.


Il semble donc que l'autre code affecte les résultats - pouvez-vous ajouter plus de votre code à la question?


Tu vas penser que je suis fou mais c'est tout mon code. J'ai même créé un tout nouveau projet java avec uniquement cette classe et ces cas de test que vous avez fournis et que vous sortez finissent toujours par devenir null.



0
votes

Je pense que la meilleure solution à long terme au problème est d'utiliser l'injection de dépendances. boot-and-bonnet a raison de ne pas voir la sortie parce que vous avez écrasé le flux System.out. Ce que vous pouvez faire pour éviter d'avoir à remplacer le flux System.out est de modifier la méthode afin de pouvoir transmettre un flux dans lequel vous pouvez écrire.

methodA(System.out);

Ensuite, vous appelez methodA comme ceci

System.out.println(output.toString());

Et maintenant vous pouvez le faire

ByteArrayOutputStream output = new ByteArrayOutputStream();
PrintStream out = new PrintStream(output);
methodA(out);

Vous pouvez également exécuter toutes les assertions dont vous avez besoin sur ce ByteArrayOutputStream.

Enfin, dans votre code, lorsque vous exécutez la méthode en dehors d'un test unitaire, vous l'appelez comme ceci

public static void methodA(PrintStream out) {
    out.println("Enter in an integer for each step.\n");
    Scanner scan = new Scanner(System.in);
    int currentStepCount = 0;
    int totalStepCount = 0;

    for (int step = 10; step <= 16; step++) {
        if (step == 15)
            continue; // Skip fifteenth step

        out.println("\nStep " + step + ": ");
        currentStepCount = scan.nextInt();

        totalStepCount += currentStepCount;
    }

    out.println("\n\nAll steps have been taken in.\n");
    out.println("Total step count comes out to: " + totalStepCount + "\n");
    out.println("The last step count was " + currentStepCount + ". \n");
}


2 commentaires

Salut, merci pour la réponse! La même chose m'arrive ici, comme avec le code boot-and-bonnet. output.toString () est nul pour une raison quelconque.


@JustWorkAlready Avez-vous passé la variable que j'ai déclarée ci-dessus dans la méthode?