0

I am trying to make a test suite for my game. But I don't know where to begin and how test this whole class with mockito. Can somebody help me with that?

First, I mocked the board and level makers then I have done some test to parse a map with enemies, walls and a player on it and then call the parser to parse the map and then verify whether the the board and level maker methods were called, but I don't know how to continue from here. Also I have some problems mocking the Board class because its constructor has no access modifier so I can't access it when my test class is not in the same package ( how can I workd-around this problem).

Here is the parser class that I want to test:

    public class Parser {

    private LevelMaker levelMaker;

    private BoardMaker boardMaker;

    public Parser(LevelMaker levelMaker, BoardMaker boardMaker) {
        this.levelMaker = levelMaker;
        this.boardMaker = boardMaker;
    }

    /**
     * Makes Board on a level from txt file of a map.
     *
     * Supported characters:
     * ' ' an empty square.
     * '#' a wall.
     * '.' coins to collect.
     * 'P' square for players.
     * 'G' a square with a gamer (enemy in the game).
     *
     * @param map The text representation of the board
     * @return The level as represented by text map.
     */
    public Level parsingTheMap(char[][] map) {
        Square[][] grid = new Square[map.length][map[0].length];

        List<Enemy> enemies = new ArrayList<>();
        List<Square> startPositions = new ArrayList<>();

        makeGrid(map, map.length, map[0].length, grid, enemies, startPositions);

        Board board = boardMaker.createBoardFromGrid(grid);
        Level theLevel = levelMaker.createLevel(board, enemies, startPositions);
        return theLevel;
    }

    private void makeGrid(char[][] map, int width, int height,
                          Square[][] grid, List<Enemy> enemies, List<Square> startPositions) {
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                char c = map[x][y];
                addSquare(grid, enemies, startPositions, x, y, c);
            }
        }
    }

    /**
     * Adds a square to the grid based on a given character. These
     * character come from the map files and describe the type
     * of square.
     *
     * @param grid The grid of squares with board[x][y] being the
     *            square at column x, row y.
     * @param enemies List of all enemies that were added to the map.
     * @param startPositions List of all start positions that were added
     *            to the map.
     * @param x coordinate of the square.
     * @param y coordinate of the square.
     * @param c Character describing the square type.
     */
    protected void addSquare(Square[][] grid, List<Enemy> enemies,
                             List<Square> startPositions, int x, int y, char c) {
        switch (c) {
            case ' ':
                grid[x][y] = boardMaker.createEmptySquare();
                break;
            case '#':
                grid[x][y] = boardMaker.createWall();
                break;
            case '.':
                Square coinsSquare = boardMaker.createEmptySquare();
                grid[x][y] = coinsSquare;
                levelMaker.createCoin().occupying(coinsSquare);
                break;
            case 'G':
                Square enemySquare = makeEnemySquare(enemies, levelMaker.createEnemy());
                grid[x][y] = enemySquare;
                break;
            case 'P':
                Square playerSquare = boardMaker.createEmptySquare();
                grid[x][y] = playerSquare;
                startPositions.add(playerSquare);
                break;
            default:
                throw new GameConfigurationException("Invalid character at "
                        + x + "," + y + ": " + c);
        }
    }

    /**
     * creates a Square with the specified enemy on it
     * and appends the placed enemy into the enemy list.
     *
     * @param enemies all the enemies in the level
     * @param enemy the newly created enemy to be placed
     * @return a square with the enemy on it.
     */
    protected Square makeEnemySquare(List<Enemy> enemies, Enemy enemy) {
        Square enemySquare = boardMaker.createEmptySquare();
        enemies.add(enemy);
        enemy.occupying(enemySquare);
        return enemySquare;
    }

    /**
     * Parses the list of strings into a 2-dimensional character array and
     * passes it on to parsingTheMap(char[][])
     *
     * @param text The plain text, with every entry in the list being a equally
     *            sized row of squares on the board and the first element being
     *            the top row.
     * @return The level as represented by the text.
     */
    public Level parsingTheMap(List<String> text) {

        int height = text.size();
        int width = text.get(0).length();

        char[][] map = new char[width][height];
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                map[x][y] = text.get(y).charAt(x);
            }
        }
        return parsingTheMap(map);
    }


    /**
     * Parses the provided input stream as a character stream and passes it
     * result to parsingTheMap(List).
     *
     * @param src The input stream that will be read.
     * @return The parsed level as represented by the text on the input stream.
     * @throws IOException
     */
    public Level parsingTheMap(InputStream src) throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                src, "UTF-8"))) {
            List<String> linesOfMap = new ArrayList<>();
            while (reader.ready()) {
                lines.add(reader.readLine());
            }
            return parsingTheMap(linesOfMap);
        }
    }

    /**
     * Parses the provided input stream as a character stream and passes it
     * result to parsingTheMap(List).
     *
     * @param map Name of a resource that will be read.
     * @return The parsed level as represented by the text on the input stream.
     * @throws IOException 
     */
    public Level parsingTheMap(String map) throws IOException {
        try (InputStream boardStream = parsingTheMap.class.getResourceAsStream(map)) {
            if (boardStream == null) {
                throw new GameConfigurationException("Something went wrong: " + map);
            }
            return parsingTheMap(boardStream);
        }
    }


    protected BoardMaker getBoardMaker() {
        return boardMaker;
    }
}

My test so far:

public class ParserTest {

private BoardMaker boardMaker;
private LevelMaker levelMaker;
private Parser parser;

@BeforeEach
void setUp() {
    levelMaker = mock(LevelMaker.class);
    boardMaker = mock(BoardMaker.class);

    parser = new Parser(levelMaker, boardMaker);
}

@Test
void parserTest() {
    List<String> map = Lists.newArrayList(
            "##########",
            "#........#",
            "#P      G#");

    Square square = new Square();

    Board board = mock(Board.class);
    Level level = mock(Level.class);
    Coin coin = mock(Coin.class);

    when(boardMaker.createWall()).thenReturn(square);
    when(boardMaker.createEmptySquare()).thenReturn(square);
    when(levelMaker.createCoin()).thenReturn(coin);
    when(boardMaker.createBoard(any(Square[][].class))).thenReturn(board);
    when(levelMaker.createLevel(any(Board.class), anyList(), anyList()))
            .thenReturn(level);

    //the level to compare with returned level from mapParser
    Level Level = parser.parsingTheMap(map);

    verify(boardMaker).createBoard(any(Square[][].class));
    verify(boardMaker).createEmptySquare();
    verify(boardMaker).createWall();
    verify(levelMaker).createLevel(any(Board.class), anyList(), anyList());
    verify(levelMaker).createCoin().occupying(any(Square.class));
}
}
Lex
  • 1
  • 1
  • Hint: the *default* best practice for Java unit testing is that your production and test code sit in the **same** package. Typically, in two different folders (main vs test or something alike), but in the same package! Beyond that, your question is pretty broad. We help with specific questions on programming, not with "here is my code base, and here are my tests, how to continue from here on". – GhostCat May 28 '19 at 09:35
  • Beyond that: that is why people recommend TDD. When you think "I have to write a small test **first**", then you dont end up with "all my production code is written, now how do I test it" (which is always a boring, and often *hard* experience because you notice "code is hard to test" ... when it is too late). – GhostCat May 28 '19 at 09:37

0 Answers0