HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>Большинство тестов на одну и ту же функциональность сильно похожи друг на друга. Особенно в части начальной подготовки данных. В уроке по модульным тестам каждый тест начинался со строчки: Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();. Это ещё не дублирование, но уже шаг в эту сторону. Как правило, реальные тесты сложнее и включают в себя большую подготовительную работу.</p>
1 <p>Большинство тестов на одну и ту же функциональность сильно похожи друг на друга. Особенно в части начальной подготовки данных. В уроке по модульным тестам каждый тест начинался со строчки: Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();. Это ещё не дублирование, но уже шаг в эту сторону. Как правило, реальные тесты сложнее и включают в себя большую подготовительную работу.</p>
2 <p>Допустим, мы разрабатываем собственную коллекцию, такую же как<a>ArrayList</a>и хотим протестировать методы для обработки коллекций:</p>
2 <p>Допустим, мы разрабатываем собственную коллекцию, такую же как<a>ArrayList</a>и хотим протестировать методы для обработки коллекций:</p>
3 <ul><li>contains</li>
3 <ul><li>contains</li>
4 <li>get</li>
4 <li>get</li>
5 <li>remove</li>
5 <li>remove</li>
6 <li>и другие (всего их более 20 штук)</li>
6 <li>и другие (всего их более 20 штук)</li>
7 </ul><p>Для работы этих методов нужна заранее подготовленная коллекция. Проще всего придумать одну, которая подойдёт для тестирования большинства или даже всех методов:</p>
7 </ul><p>Для работы этих методов нужна заранее подготовленная коллекция. Проще всего придумать одну, которая подойдёт для тестирования большинства или даже всех методов:</p>
8 <p>Теперь представьте, что таких тестов несколько десятков (в реальности их сотни). Код с инициализацией и наполнением коллекции начнёт кочевать из одного места в другое, порождая всё больше и больше копипасты. И если мы ещё можем вынести инициализацию коллекции на уровень класса, т.е. сделать её полем, то с наполнением её данными такой "финт" уже не пройдёт. Нам необходимо вынести эти действия в отдельный метод.</p>
8 <p>Теперь представьте, что таких тестов несколько десятков (в реальности их сотни). Код с инициализацией и наполнением коллекции начнёт кочевать из одного места в другое, порождая всё больше и больше копипасты. И если мы ещё можем вынести инициализацию коллекции на уровень класса, т.е. сделать её полем, то с наполнением её данными такой "финт" уже не пройдёт. Нам необходимо вынести эти действия в отдельный метод.</p>
9 <p>Это простое решение убирает ненужное дублирование, хотя сам метод init() нам по-прежнему нужно вызывать внутри каждого из тестовых методов.</p>
9 <p>Это простое решение убирает ненужное дублирование, хотя сам метод init() нам по-прежнему нужно вызывать внутри каждого из тестовых методов.</p>
10 <p>Тесты в JUnit обладают одной очень важной особенностью. Посмотрите на код ниже и подумайте одинаковые или разные значения выведут в консоль первый и второй тесты? Вот сам код:</p>
10 <p>Тесты в JUnit обладают одной очень важной особенностью. Посмотрите на код ниже и подумайте одинаковые или разные значения выведут в консоль первый и второй тесты? Вот сам код:</p>
11 <p>Подвох тут в том, что переменная startTime инициализируется далеко не один раз. JUnit устроен так, что создаёт новый экземпляр класса для каждого теста (метода, помеченного аннотацией @Test). То есть переменная startTime будет инициализирована при создании экземпляра для теста testFirst(), а также при создании собственного экземпляра класса TestClass для теста testSecond(). Поэтому между нестатичными полями в разных тестах не будет совершенно никакой связи.</p>
11 <p>Подвох тут в том, что переменная startTime инициализируется далеко не один раз. JUnit устроен так, что создаёт новый экземпляр класса для каждого теста (метода, помеченного аннотацией @Test). То есть переменная startTime будет инициализирована при создании экземпляра для теста testFirst(), а также при создании собственного экземпляра класса TestClass для теста testSecond(). Поэтому между нестатичными полями в разных тестах не будет совершенно никакой связи.</p>
12 <p>Следующий вопрос - какой из тестов запустится раньше? То есть у которого из тестов значение переменной startTime будет меньше? Заранее предсказать ответ на этот вопрос практически невозможно. В этом примере сначала запустился тест testSecond(), а потом запустился тест testFirst(). Попробуйте запустить подобный пример на своём компьютере и посмотреть на результаты.</p>
12 <p>Следующий вопрос - какой из тестов запустится раньше? То есть у которого из тестов значение переменной startTime будет меньше? Заранее предсказать ответ на этот вопрос практически невозможно. В этом примере сначала запустился тест testSecond(), а потом запустился тест testFirst(). Попробуйте запустить подобный пример на своём компьютере и посмотреть на результаты.</p>
13 <p>Почему такое происходит? Именно потому, что мы отдаём бразды правления тестами фреймворку. Он будет определять оптимальный порядок и способ запуска тестов. Пользователю при этом необходимо написать тесты, которые не будут зависеть друг от друга, но об этом мы поговорим в следующем уроке.</p>
13 <p>Почему такое происходит? Именно потому, что мы отдаём бразды правления тестами фреймворку. Он будет определять оптимальный порядок и способ запуска тестов. Пользователю при этом необходимо написать тесты, которые не будут зависеть друг от друга, но об этом мы поговорим в следующем уроке.</p>
14 <p>Итак, вернёмся к вопросу о правильной инициализации нашей коллекции перед началом каждого теста. Для решения этой проблемы тестовые фреймворки предоставляют<strong>хуки</strong>- специальные методы, которые запускаются до или после тестов. Ниже пример того, как наполнить нашу коллекцию перед каждым тестом:</p>
14 <p>Итак, вернёмся к вопросу о правильной инициализации нашей коллекции перед началом каждого теста. Для решения этой проблемы тестовые фреймворки предоставляют<strong>хуки</strong>- специальные методы, которые запускаются до или после тестов. Ниже пример того, как наполнить нашу коллекцию перед каждым тестом:</p>
15 <p>Аннотацией @BeforeEach помечаются методы, которые будут выполняться перед стартом<em>каждого</em>из тестовых методов. В этих методах не обязательно создаются переменные. Возможно, инициализация заключается в подготовке файловой системы, например, созданию файлов. Но если метод должен создать данные и сделать их доступными в тестах, то придётся использовать поля класса.</p>
15 <p>Аннотацией @BeforeEach помечаются методы, которые будут выполняться перед стартом<em>каждого</em>из тестовых методов. В этих методах не обязательно создаются переменные. Возможно, инициализация заключается в подготовке файловой системы, например, созданию файлов. Но если метод должен создать данные и сделать их доступными в тестах, то придётся использовать поля класса.</p>
16 <p>Если нам нужно выполнить код один раз перед всеми тестами, его нужно выполнять внутри метода с аннотацией @BeforeAll. Этот хук запускается ровно один раз перед всеми тестами, расположенными в одном классе. При этом сам метод, помеченный аннотацией @BeforeAll должен быть уже статичным.</p>
16 <p>Если нам нужно выполнить код один раз перед всеми тестами, его нужно выполнять внутри метода с аннотацией @BeforeAll. Этот хук запускается ровно один раз перед всеми тестами, расположенными в одном классе. При этом сам метод, помеченный аннотацией @BeforeAll должен быть уже статичным.</p>
17 <p>Аналогично, существуют аннотации @AfterEach и @AfterAll, которые позволяют выполнить определённые действия после каждого или после всех тестов. Например, вы можете написать метод, который удалит созданный в начале файл.</p>
17 <p>Аналогично, существуют аннотации @AfterEach и @AfterAll, которые позволяют выполнить определённые действия после каждого или после всех тестов. Например, вы можете написать метод, который удалит созданный в начале файл.</p>
18 <p>Почему важно использовать аннотации, а не самостоятельно организовывать порядок и способ выполнения тестов? Самый простой ответ на этот вопрос такой: JUnit должен контролировать происходящие процессы и побочные эффекты в тестах. Все, что вызывается вами вручную, отрабатывается вне JUnit. Это значит, что JUnit никак не может отследить, что происходит, и в какой момент можно запускать тесты.</p>
18 <p>Почему важно использовать аннотации, а не самостоятельно организовывать порядок и способ выполнения тестов? Самый простой ответ на этот вопрос такой: JUnit должен контролировать происходящие процессы и побочные эффекты в тестах. Все, что вызывается вами вручную, отрабатывается вне JUnit. Это значит, что JUnit никак не может отследить, что происходит, и в какой момент можно запускать тесты.</p>