JavaOpt: Тестирование

JavaOpt: Тестирование


  1. Соблюдение TDD (Test-Driven Development) позволяет избегать ошибок и стабилизирует работу кода
  2. Тесты так же выжны как и продуктивный код. Основной плюс их в применении изменений - чем их больше, тем проще найти ошибку при изменениях
    3.Каждый тест должен строиться по процессу:
  • Build - подготовка данных/объектов к тесту
  • Operate - выполнение действий (позитивных или деструктивных)
  • Check - проверка результатов тестирования
  1. Тест должен легко читаться. Все тесты желательно делать по одному принципу для всего приложения
  2. Количество assert лучше сводить к минимуму
  3. 1 тест проверяет 1 действие
  4. Тесты рекомендовано FIRST:
    F - Fast - тесты должны быть быстрыми
    I - Independent - изолированными друг от друга
    R - Repeatable - должны отрабатывать одинаково при любых условиях, они не должны зависеть от внешних ресурсов (лучше использовать mock или stub)
    S - Self-Validating - недвусмысленны: либо прошли успешно, либо нет
    T - Timely - должны писаться перед продуктивным кодом
  5. Следует считать, что в коде всегда будет ошибка пока не будет наложен тест
  6. Использовать утилиты для проверки покрытия (code coverage). Например, Jacoco
  7. После обнаружения бага в продуктиве полезным будет наложение на него теста
  8. Все внешние данные внешних ресурсов должны быть вынесены за код. Сюда входят например: ответы от внешних сервисов в различных форматах, SQL запросы, бинарные документы и т.д.
  9. В именах тестовых методов следует отображать какие-то свойства, действия, настройки и т.п. - из названия должно быть понятно что будет тестироваться и при каких уникальных условиях
  10. Для простоты восприятия, для тестовых методов можно добавлять краткое описание. Например, JUnit имеет аннотацию @DisplayName(“${test_name}”) и @DisplayNameGenerator(*.class)
  11. Тестирование не говорит о корректной работе кода (если он успешно выполнен), но если тест выполнен с ошибкой - это должно свидетельством об ошибке
  12. Желательно чтобы тесты проверяли не какое-то абстрактное наличие значения чего-либо, а четкое значение. Например:
1
2
3
List<Object> x = new ArrayList();
X.add(new Object());
assertThat(x.size(), greaterThan(0));

Или хотя бы:

1
assertThat(x.isEmpty, not(true));
  1. При наличии логических или бизнес связей между несколькими тестами рекомендуется объединять их в группы:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MasterTest {
@BeforeEach
void setup() {...}

@Nested
@DisplayName("Pin tests")
public class PinTest {
@Test
void xVersusY() {...}

@Test
void unity() {...}
}

@Nested
@DisplayName(“Sensor tests”)
public class SensorTest {
@Test
void isSignalReceived() {...}
}
}
  1. Тестирование разбивается на виды:
  • Unit-тесты. Тестирование изолированных методов. Внешние ресурсы заменяются на mock или stub. Основной вид тестов
  • Integration-тесты. Тестируют взаимодействие межде компонентами в рамках приложения. Для объектов - вызов методов и работа с другими сервисами; для сервисов - используется когда код будет помещен в контейнеры, тестирует работу с БД (как правило, embedded) и прочими внешними ресурсами (как правило, их stub или mock); для подсистем - тестируется использование внешними системами (вызов внешних сервисов, предоставленных приложением).
  • System-software-тесты. Тестируют интеграцию с действующими внешними системами и ресурсами. Могут использовать mock, но желательна интеграция с действительными ресурсами в соответствующей среде
  1. Условно, тестирование можно разделить на 3 этапа:
  • Красный. Пишутся только основные интерфейсы для тестов - основа, по которой можно понять основные функции приложения или его частей. Де-факто. Это просто скелет
  • Зеленый. К методам, подготовленным на красном этапе добавляется наполнение чтобы тест выполнялся успешно. Это выполняется для того чтобы можно было контролировать процесс разработки (точнее, поддержка его в постоянно работчем состоянии)
  • Постепенно, мелкими шагами дорабатываются тесты и реализуется логика приложения. Поскольку тесты уже наложены, выполняться они будут с ошибками, которые будет легко найти и выявить причину
  1. Часто бывает полезным добавление fuzz-тестов (fuzzing) - они намеренно создают ошибочные ситуации или условно случайный набор данных. Задача - найти уязвимые места в приложении, которые изначально являются неочевидными. Генерация должна включать cлучаи успешной первичной проверки (т.е. случаи когда визуально данные корректны и проходят валидацию, но де-факто они являются ошибочными). Данные тесты можно писать самостоятельно, либо использовать библиотеки (например, Snodge)
  2. Желательно заранее продумать какие инструменты и библиотеки будут использоваться для каждого слоя тестирования, например:
  • Unit-тесты - JUnit, TestNG
  • UI-тесты - Selenium, Cypress
  • Производительность - JMeter, Gatling
  • Тест контрактов - Spring Cloud Contract, Pact
  • тесты изменений - PITest
  1. Тесты необходимо поддерживать актуальными. Если они отключаются необходимо указать причину
 Comments
Comment plugin failed to load
Loading comment plugin