Лабораторная работа № XII : Юнит-тестирование и обеспечение качества кода.

(проект лабораторной , будет правиться по результатам приемки лаб 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 = хороший проект

Вместо этого даётся задача:

  1. получить отчёт;
  2. найти непокрытую ветвь;
  3. решить, значима ли она;
  4. добавить тест, если она соответствует реальному поведению;
  5. показать пример, где строка выполнена тестом, но assertion ничего полезного не проверяет.

То есть coverage используется как инструмент поиска пробелов, а не как синоним качества.

Финальный вопрос:

Что полезнее: один тест с десятью случайными assertions или несколько тестов с осмысленными именами, проверяющих отдельные сценарии?

Итоговый результат ЛР 


mvn test — green
mvn verify — green
JUnit report — есть
JaCoCo report — есть
production code не зависит от test classes
service тестируется без реальной БД
исключения тестируются
критические boundary cases тестируются


Вопросы для углубления: 
Что такое модульное тестирование и какую
проблему оно решает?Базовая структура теста в
JUnit и роль assertions. Что такое TDD и когда оно
оправдано?Что означает «покрытие кода тестами»
и как его правильно интерпретировать?Какие
практики повышают тестопригодность кода?
Автор: к.п.н., Румянцев Сергей Александрович, доцент Финансового университета при Правительстве РФ; доцент ОЧУВО Международного инновационного университета; Консалтинг, управление разработкой ПО; системный и бизнес анализ; менеджмент; аналитиз данных; управление ИТ. Телефон для связи +79269444818 (мессенджеры)   Короткая ссылка:

(проект лабораторной , будет правиться по результатам приемки лаб 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 = хороший проект

Вместо этого даётся задача:

  1. получить отчёт;
  2. найти непокрытую ветвь;
  3. решить, значима ли она;
  4. добавить тест, если она соответствует реальному поведению;
  5. показать пример, где строка выполнена тестом, но assertion ничего полезного не проверяет.

То есть coverage используется как инструмент поиска пробелов, а не как синоним качества.

Финальный вопрос:

Что полезнее: один тест с десятью случайными assertions или несколько тестов с осмысленными именами, проверяющих отдельные сценарии?

Итоговый результат ЛР 


mvn test — green
mvn verify — green
JUnit report — есть
JaCoCo report — есть
production code не зависит от test classes
service тестируется без реальной БД
исключения тестируются
критические boundary cases тестируются


Вопросы для углубления: 
Что такое модульное тестирование и какую
проблему оно решает?Базовая структура теста в
JUnit и роль assertions. Что такое TDD и когда оно
оправдано?Что означает «покрытие кода тестами»
и как его правильно интерпретировать?Какие
практики повышают тестопригодность кода?
https://webprogr.ru/~JBfoQ
Короткая ссылка на новость:https://webprogr.ru/~JBfoQ


Последние новости

Лабораторная работа № XII : Юнит-тестирование и обеспечение качества кода.

(проект лабораторной , будет правиться по результатам приемки лаб 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 = хороший проект

Вместо этого даётся задача:

  1. получить отчёт;
  2. найти непокрытую ветвь;
  3. решить, значима ли она;
  4. добавить тест, если она соответствует реальному поведению;
  5. показать пример, где строка выполнена тестом, но assertion ничего полезного не проверяет.

То есть coverage используется как инструмент поиска пробелов, а не как синоним качества.

Финальный вопрос:

Что полезнее: один тест с десятью случайными assertions или несколько тестов с осмысленными именами, проверяющих отдельные сценарии?

Итоговый результат ЛР 


mvn test — green
mvn verify — green
JUnit report — есть
JaCoCo report — есть
production code не зависит от test classes
service тестируется без реальной БД
исключения тестируются
критические boundary cases тестируются


Вопросы для углубления: 
Что такое модульное тестирование и какую
проблему оно решает?Базовая структура теста в
JUnit и роль assertions. Что такое TDD и когда оно
оправдано?Что означает «покрытие кода тестами»
и как его правильно интерпретировать?Какие
практики повышают тестопригодность кода?
Рейтинг@Mail.ru