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

Time:10-28

I'm attempting to make a Tic-Tac-Toe class and unit test all of the methods. Right now, my class file looks as follows:

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
}

If I wanted to test that printBoard() works when the entire board is full, how would I do that without making calls to makeMove() or making all of my class variables public?

Currently, my testing file looks as follows:

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
}

I saw something about using @Jailbreak in Manifold, but I couldn't get that working. If it helps anyone, I'm using IntelliJ. Thank you for any help ya'll!

CodePudding user response:

Three optional options (there are more):

  1. Create a second constructor of TicTacToe where you can pass into a prefilled board public TicTacToe(char[][] board)
  2. Add an extra method public void loadBoard(char[][] board).
  3. Use reflection to fill the board variable. (bad option because this can results in another state)
  4. Add a package-private constructor and let both classes reside in the same package.
  5. Create package-private setBoard method and access it in the arrange-part of your code.

You can also do something with partical mocking but I think thats also not a good option in this situation.

Another good option is to create a separate class for printing out the board:

public class BoardPrinter {
      
    public BoardPrinter() {}

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