Модульное тестирование действительно важно для качества кода (и для облегчения вашей жизни при поиске ошибок), но если вы никогда раньше этим не занимались, с чего начать? Надеюсь, этот урок поможет.

Давайте начнем

Давайте используем веб-игру TicTacToe на Java, чтобы продемонстрировать, как написать свой первый модульный тест. Во-первых, вот код, который проверяет, выиграл ли кто-нибудь игру.

public Player whoHasWon() {
    ArrayList<Coordinate[]> winningPositions = new ArrayList<>();
    // rows
    winningPositions.add(new Coordinate[] {new Coordinate(0,0), new Coordinate(1,0), new Coordinate(2,0)});
    winningPositions.add(new Coordinate[] {new Coordinate(0,1), new Coordinate(1,1), new Coordinate(2,1)});
    winningPositions.add(new Coordinate[] {new Coordinate(0,2), new Coordinate(1,2), new Coordinate(2,2)});
    // columns
    winningPositions.add(new Coordinate[] {new Coordinate(0,0), new Coordinate(0,1), new Coordinate(0,2)});
    winningPositions.add(new Coordinate[] {new Coordinate(1,0), new Coordinate(1,1), new Coordinate(1,2)});
    winningPositions.add(new Coordinate[] {new Coordinate(2,0), new Coordinate(2,1), new Coordinate(2,2)});
    // diagonals
    winningPositions.add(new Coordinate[] {new Coordinate(0,0), new Coordinate(1,1), new Coordinate(2,2)});
    winningPositions.add(new Coordinate[] {new Coordinate(2,0), new Coordinate(1,1), new Coordinate(0,2)});

    for (Coordinate[] winningPosition : winningPositions) {
        if (getCell(winningPosition[0]) == getCell(winningPosition[1])
                && getCell(winningPosition[1]) == getCell(winningPosition[2])) {
            if (getCell(winningPosition[0]) != null) {
                return getCell(winningPosition[0]);
            }
        }
    }
    return null;
}

Затем мы должны написать несколько модульных тестов, чтобы убедиться, что поведение корректно и не сломается в будущем. Так с чего же начать?

Нам нужно будет внедрить тестовый фреймворк в проект. В этом примере мы будем использовать JUnit. Чтобы сделать это в Maven, просто добавьте следующее в свои зависимости в pom.xml

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.8.2</version>
  <scope>test</scope>
</dependency>

Затем пришло время создать класс, в котором будут существовать модульные тесты. Мы хотим, чтобы тесты находились в том же пакете, что и класс, который мы тестируем. В нашем примере это пакет com.diffblue.javademo.tictactoe;

Это означает, что файл должен находиться в папке com/diffblue/javademo/tictactoe.

Для проекта Maven это должно иметь префикс src/test/java.

Поэтому путь от корня проекта — src/test/java/com/diffblue/javademo/tictactoe.

Это означает, что все тестовые классы хорошо отделены от основного исходного кода.

Наконец, класс должен соответствовать формату ‹тестируемый класс›Test, что в данном примере означает, что класс будет BoardTest, а файл, очевидно, BoardTest.java.

Примечание. При выполнении тестов с помощью Maven по умолчанию выполняется поиск классов с суффиксом Test.

Имея все это в виду, создайте класс:

package com.diffblue.javademo.tictactoe;

public class BoardTest {
}

В этом примере давайте создадим тест, чтобы проверить, правильно ли работает обнаружение игрока, выигравшего через верхнюю строку.

Создайте метод для этого теста:

package com.diffblue.javademo.tictactoe;

import org.junit.Test;

public class BoardTest {

  @Test
  public void playerOTopRow() {
    // Arrange

    // Act

    // Assert
  }
}

Это немного отличается от методов, которые вы написали в своем исходном коде. Отработка метода: Сначала идет аннотация, говорящая, что это тест:

@Test

Также обратите внимание на соответствующий импорт. Это сообщит инструментам, что этот метод является тестом. Если вы используете IntelliJ или Eclipse, вы увидите, что теперь вы можете запустить этот метод в качестве теста.

Самое время отметить, что модульные тесты предназначены для защиты от ошибок в будущем. Это означает, что ваш тест должен быть легко читаемым и понятным как другим разработчикам, так и вам в будущем. Чтобы разделить тесты для удобства чтения, давайте добавим три комментария: упорядочивать, действовать и утверждать.

Теперь приступим к созданию теста!

Нам нужно настроить среду, в которой верхняя строка доски — это нули:

package com.diffblue.javademo.tictactoe;

import org.junit.Test;

public class BoardTest {

  @Test
  public void playerOTopRow() {
    // Arrange
    Board myBoard = new Board();
    myBoard.setCell(new Coordinate(0,0), Player.NOUGHT);
    myBoard.setCell(new Coordinate(0,1), Player.CROSS);
    myBoard.setCell(new Coordinate(1,0), Player.NOUGHT);
    myBoard.setCell(new Coordinate(1,1), Player.CROSS);
    myBoard.setCell(new Coordinate(2,0), Player.NOUGHT);

    // Act

    // Assert
  }
}

Теперь у нас есть доска, на которой нолики побеждают в верхнем ряду. Затем вызовите метод whoHasWon() и соберите результат.

package com.diffblue.javademo.tictactoe;

import org.junit.Test;

public class BoardTest {

  @Test
  public void playerOTopRow() {
    // Arrange
    Board myBoard = new Board();
    myBoard.setCell(new Coordinate(0,0), Player.NOUGHT);
    myBoard.setCell(new Coordinate(0,1), Player.CROSS);
    myBoard.setCell(new Coordinate(1,0), Player.NOUGHT);
    myBoard.setCell(new Coordinate(1,1), Player.CROSS);
    myBoard.setCell(new Coordinate(2,0), Player.NOUGHT);

    // Act
    Player result = myBoard.whoHasWon();

    // Assert
  }
}

Теперь у нас есть тест, который настраивает среду и вызывает тестируемый метод.

Остался последний шаг для завершения теста: добавить Assert. Утверждение является ключевой частью теста, это то, что проверяется, чтобы сказать, прошел ли тест или нет. Здесь мы проверяем, что результат равен нулю.

Вот наш полный тест:

package com.diffblue.javademo.tictactoe;

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class BoardTest {

  @Test
  public void playerOTopRow() {
    // Arrange
    Board myBoard = new Board();
    myBoard.setCell(new Coordinate(0,0), Player.NOUGHT);
    myBoard.setCell(new Coordinate(0,1), Player.CROSS);
    myBoard.setCell(new Coordinate(1,0), Player.NOUGHT);
    myBoard.setCell(new Coordinate(1,1), Player.CROSS);
    myBoard.setCell(new Coordinate(2,0), Player.NOUGHT);

    // Act
    Player result = myBoard.whoHasWon();

    // Assert
    assertEquals("Player O didn't win in the top row", Player.NOUGHT, result);
  }
}

Глядя на утверждение, обратите внимание на сообщение (первый аргумент утверждения). Когда тест не пройден, это сообщение печатается в результатах. Это может дать четкое представление о том, что пошло не так, человеку, отлаживающему тесты.

Еще одно замечание: порядок аргументов - сначала ожидаемый результат, а затем фактический результат. Поскольку инструменты будут включать ожидаемые и фактические результаты в выходные данные, важно избежать путаницы, получая их правильным путем.

Закончив тест, теперь мы можем запустить все тесты, используя mvn test, и мы увидим, что наш тест проходит:

[INFO] ------------------------------------------------------

[INFO]  T E S T S

[INFO] -------------------------------------------------------

[INFO] Running com.diffblue.javademo.tictactoe.BoardTest

[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.018 s - in com.diffblue.javademo.tictactoe.BoardTest

[INFO]

[INFO] Results:

[INFO]

[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO]

Поздравляю! Итак, вы написали свой первый тест. Полный исходный код этого урока доступен здесь.

Вас также может заинтересовать: