为什么使用 ByteArrayOutputStream 的 JUnit 测试在运行多个测试时会失败,但在单独运行时会通过?

问题描述 投票:0回答:1

我目前正在使用 JUnit 测试,该测试使用 ByteArrayInput 流向我的类提供输入,并使用 BytearrayOutputStream 获取输出并将其与预期输出进行比较。 当单独运行测试时,它们可以很好地通过,但是当测试整个测试类时,只有第一个通过,后续的失败,因为 OutputStream 中没有任何内容。

这是正在测试的类:

package org.oscargs;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;


@FunctionalInterface
interface Function {
    void apply();
}
public class Menu {
    private Function[] players;
    private Logger logger;
    private Scanner scan;


//    private final PrintStream printStream;

    Menu() {
        logger = LogManager.getLogger();
        players = new Function[2];
    }

    public void selectInputStream(final InputStream inputStream) {
        this.scan = new Scanner(inputStream, StandardCharsets.UTF_8);
    }

    public void teardown() {
        this.scan = null;
        this.logger = null;
        this.players = null;
    }

    public void showMenu() {
        logger.info("Input command: ");
        String input = scan.nextLine();
        String[] parameters = input.split(" ");

        if (parameters.length == 1) {
            if (!parameters[0].equals("exit")){
                logger.info("Bad parameters! Only available parameters are 'start <player1> <player2>' and 'exit'\n");
                return;
            }

            logger.info("Bye\n");
//            Main.endGame();
//            System.exit(0);

        } else if (parameters.length == 3) {
            if (! parameters[0].equals("start")){
                logger.info("Bad parameters! Only available parameters are 'start <player1> <player2>' and 'exit'\n");
                return;
            }

            for (int i = 0; i < 2; i++) {
                char p = Constants.getPlayerChar(i);
                switch (parameters[i+1]) {
                    case "easy":
                        players[i] = () -> ComputerPlayer.moveEasy(p, Constants.EASY);
                        break;
                    case "medium":
                        players[i] = () -> ComputerPlayer.moveMedium(p);
                        break;
                    case "hard":
                        players[i] = () -> ComputerPlayer.moveHard(p);
                        break;
                    case "user":
                        players[i] = () -> Player.move(p);
                        break;
                    default:
                        logger.info("Bad parameters!");
                        return;
                }
            }

            Game.init();

            while(Game.continueGame) {
                Game.game(players[0], players[1]);
            }

        } else {
            logger.info("Bad parameters!");
        }
    }
}

这是我的测试课


package org.oscargs;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

import java.io.*;

import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
class MenuTest {
    private Menu menu;
    private OutputStream outputStream;



    @BeforeEach
    void setUp() {
        try {
            this.outputStream = new ByteArrayOutputStream();
            this.outputStream.flush();
            System.setOut(new PrintStream(outputStream));
////        this.inputStream = new ByteArrayInputStream();
            this.menu = new Menu();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @AfterEach
    void tearDown() {
        try {
            this.outputStream.flush();
            this.outputStream.close();
            this.outputStream = null;
            System.setOut(null);

//            this.inputStream.close();

            this.menu.teardown();
            this.menu = null;

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    void badCommandLenghtOneTest() {
        // Given
        String expectedOutput = "";
        String input = "badcommand"+ System.lineSeparator();
        ByteArrayInputStream in = new ByteArrayInputStream(input.getBytes());

        menu.selectInputStream(in);
        menu.showMenu();

        String givenOutput = outputStream.toString();

        expectedOutput += "Input command: ";
        expectedOutput += "Bad parameters! Only available parameters are 'start <player1> <player2>' and 'exit'\n";

        assertEquals(expectedOutput, givenOutput);
    }

    @Test
    void exitCommandTest() {
        // Given
        String expectedOutput = "";
        String input = "exit"+ System.lineSeparator();
        ByteArrayInputStream in = new ByteArrayInputStream(input.getBytes());

        menu.selectInputStream(in);
        menu.showMenu();

        String givenOutput = outputStream.toString();

        expectedOutput += "Input command: ";
        expectedOutput += "Bye\n";

        assertEquals(expectedOutput, givenOutput);
    }
}

这是错误日志

-------------------------------------------------------------------------------
Test set: org.oscargs.MenuTest
-------------------------------------------------------------------------------
Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.452 s <<< FAILURE! -- in org.oscargs.MenuTest
org.oscargs.MenuTest.badCommandLenghtOneTest -- Time elapsed: 0.008 s <<< FAILURE!
org.opentest4j.AssertionFailedError: 
expected: <Input command: Bad parameters! Only available parameters are 'start <player1> <player2>' and 'exit'
> but was: <>
    at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
    at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
    at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
    at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
    at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
    at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1141)
    at org.oscargs.MenuTest.badCommandLenghtOneTest(MenuTest.java:64)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

我正在使用 Java 17 和 JUnit 5.10.0

<dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.10.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.10.0</version>
            <scope>test</scope>
        </dependency>

我已经尝试在执行每个测试后关闭流并将它们设置为空,以防止任何可能干扰的泄漏和打开流,并将被测类中的扫描和记录器字段设置为空,但似乎没有影响。

提前致谢

java testing junit outputstream
1个回答
0
投票

我怀疑您有某种状态从一个测试“渗透”到另一个测试(例如,在一个测试中修改然后在另一个测试中读取的静态)。

你应该重构你的类,以便

  1. 菜单是不可变的 - 如果您想要新值,请丢弃旧的
    Menu
    并创建一个
    new Menu(...)
  2. 游戏有零个静态方法 - 因此您创建一个
    new Game(player1, player2)
    实例并调用它的方法。要开始新游戏,您可以调用
    gameInstance.reset()
    或创建一个
    new Game()
© www.soinside.com 2019 - 2024. All rights reserved.