如何在 class 上 运行 在 Jupiter 中进行单元测试而不调用其他方法来更改对象?

How to run unit tests in Jupiter on a class without calling other methods to alter the object?

我正在尝试制作 Tic-Tac-Toe class 并对所有方法进行单元测试。现在,我的 class 文件如下所示:

public class TicTacToe {
    public static final Character PLAYER_1 = 'X';
    public static final Character PLAYER_2 = 'O';
    public static final Integer BOARD_SIZE = 3;

    private char[][] board;
    private char currentPlayer;

    public TicTacToe() {
        board = new char[BOARD_SIZE][BOARD_SIZE];
        initializeBoard();
        currentPlayer = PLAYER_1;
    }

    public void initializeBoard() {
        for(char[] row: board) {
            Arrays.fill(row, '-');
        }
    }

    public void printBoard() {
        for(int i = 0; i < BOARD_SIZE; i++) {
            for(int j = 0; j < BOARD_SIZE; j++) {
                System.out.print(board[i][j]);
                if(j == BOARD_SIZE - 1) {
                    System.out.println();
                }
                else {
                    System.out.print(" ,");
                }
            }
        }
    }

    public boolean makeMove(int x, int y) {
        if(x >= BOARD_SIZE || y >= BOARD_SIZE || x < 0 || y < 0 || board[x][y] != '-') {
            return false;
        }
        board[x][y] = currentPlayer;
        currentPlayer = currentPlayer == PLAYER_1 ? PLAYER_2 : PLAYER_1;
        return true;
    }
// more methods are below
}

如果我想测试 printBoard() 在整个电路板已满时是否正常工作,我该怎么做而不调用 makeMove() 或使我的所有 class 变量 public ?

目前,我的测试文件如下所示:

import org.junit.*;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.*;

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

class TicTacToeTest {
    private final PrintStream standardOut = System.out;
    private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();

    @BeforeEach
    public void setUp() {
        System.setOut(new PrintStream(outputStreamCaptor));
    }

    @org.junit.jupiter.api.Test

    @AfterEach
    public void tearDown() {
        System.setOut(standardOut);
    }

    @Test
    void initializeBoard() {
    }

    @Test
    void printBoard() {
        TicTacToe game = new TicTacToe();
        game.printBoard();

        assertEquals("- ,- ,-\r\n- ,- ,-\r\n- ,- ,-", outputStreamCaptor.toString()
                .trim());

        // I want to test that a full board works for printBoard() right here.

    }
// more testing methods below
}

我看到了有关在 Manifold 中使用 @Jailbreak 的信息,但我无法使其正常工作。如果它对任何人有帮助,我正在使用 IntelliJ。谢谢你的帮助!

三个可选选项(还有更多):

  1. 创建 TicTacToe 的第二个构造函数,您可以在其中传递到预填板 public TicTacToe(char[][] board)
  2. 添加一个额外的方法public void loadBoard(char[][] board)
  3. 使用反射填充板变量。 (错误的选择,因为这会导致另一种状态)
  4. 添加一个包私有的构造函数,让两个 class 位于同一个包中。
  5. 创建包私有的 setBoard 方法并在代码的排列部分访问它。

您也可以使用部分模拟来做一些事情,但我认为在这种情况下这也不是一个好的选择。

另一个不错的选择是创建一个单独的 class 用于打印电路板:

public class BoardPrinter {
      
    public BoardPrinter() {}

    public printBoard(char[][] board} {
      // print the board
    }
}