(проект лабораторной , будет правиться по результатам приемки лаб 11)
Лабораторная работа № 12 — «JUnit, TDD, mocks и качество кода»
Последняя работа должна замкнуть весь семестр: студент тестирует не новый учебный Calculator, а собственные Transaction, WalletService, Repository, FeeStrategy.
На текущую дату актуальна стабильная например ветка JUnit 6.1; JUnit 6.1.3 . JUnit 6 имеет Java 17 baseline, поэтому также совместим с выбранным JDK 25. Если вышла более актуальная - используйте ее.
Структура Maven-проекта уже подготовлена с ЛР №2:
src/main/java src/test/java
Студент создаёт:
class WalletServiceTest { @BeforeEach void setUp() { ... } @Test void shouldTransferMoneyBetweenWallets() { // arrange // act // assert } }
Обязательные виды тестов:
обычный позитивный test; граничные значения; негативный сценарий; assertThrows; parameterized test; тест equals/hashCode; тест сортировки; тест Strategy; тест сервиса с fake/mock repository.
Пример:
@Test void transferShouldFailWhenBalanceIsInsufficient() { var exception = assertThrows( InsufficientFundsException.class, () -> service.transfer( source, target, new BigDecimal("100000") ) ); assertEquals( oldBalance, source.getBalance() ); }
Главное здесь не только проверить исключение, но и убедиться, что после него не нарушено состояние.
Parameterized tests.
Например, проверка суммы:
@ParameterizedTest @CsvSource({ "0, false", "-1, false", "0.01, true", "1000, true" }) void amountValidation( String amount, boolean valid ) { ... }
Mocking.
Для TransactionService создаётся mock TransactionRepository. Mockito 5 остаётся текущей основной веткой проекта
Задача:
repository возвращает заранее заданную транзакцию; service вызывает repository; проверяется результат; проверяется взаимодействие.
Но студент должен ответить:
Почему не нужно mock'ать
BigDecimal,ArrayListи собственный простой value object?
Тем самым mocks преподаются как изоляция значимой внешней зависимости, а не как обязательный ингредиент каждого теста.
Мини-TDD.
Теперь надо выполнить с помощью ИИ новое требование:
операция расхода свыше заданного дневного лимита должна отклоняться.
Разработка выполняется строго:
RED пишем падающий тест → GREEN минимальная реализация → REFACTOR улучшаем структуру без изменения поведения
В отчёт вставляется история из трёх небольших Git-коммитов.
Coverage.
Подключается JaCoCo и выполняется:
./mvnw test ./mvnw verify
JaCoCo измеряет несколько типов покрытия, включая instructions, branches, lines, methods и classes.
Но студентам нельзя выставлять правило:
100% coverage = хороший проект
Вместо этого даётся задача:
- получить отчёт;
- найти непокрытую ветвь;
- решить, значима ли она;
- добавить тест, если она соответствует реальному поведению;
- показать пример, где строка выполнена тестом, но assertion ничего полезного не проверяет.
То есть coverage используется как инструмент поиска пробелов, а не как синоним качества.
Финальный вопрос:
Что полезнее: один тест с десятью случайными assertions или несколько тестов с осмысленными именами, проверяющих отдельные сценарии?
Итоговый результат ЛР
mvn test — green mvn verify — green JUnit report — есть JaCoCo report — есть production code не зависит от test classes service тестируется без реальной БД исключения тестируются критические boundary cases тестируются
Вопросы для углубления:
Что такое модульное тестирование и какую
проблему оно решает?Базовая структура теста в
JUnit и роль assertions. Что такое TDD и когда оно
оправдано?Что означает «покрытие кода тестами»
и как его правильно интерпретировать?Какие
практики повышают тестопригодность кода?
