Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Понятные имена и вложенные классы

Когда тестов становится много, плоский список методов в одном классе читать трудно. В JUnit есть два инструмента. @DisplayName даёт тесту понятное имя с пробелами и любыми символами, в том числе по-русски. @Nested группирует тесты во внутренние классы по ситуациям: «при создании поста», «при публикации поста». Вместе они превращают отчёт о тестах в описание того, как ведёт себя класс.

@DisplayName

// src/test/java/com/example/blog/PostServiceNestedTest.java
@Test
@DisplayName("отклоняет пустой заголовок")
void rejectsEmptyTitle() {
    assertThrows(IllegalArgumentException.class, () -> service.create(" ", "anna"));
}

IDE показывает «отклоняет пустой заголовок» вместо имени метода. Имя метода по-прежнему важно: Maven использует его в отчётах, и вы видите его в стеке вызовов.

@Nested

Вложенный тестовый класс — нестатический внутренний класс с аннотацией @Nested. Весь тестовый класс:

// src/test/java/com/example/blog/PostServiceNestedTest.java
package com.example.blog;
 
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
 
import static org.junit.jupiter.api.Assertions.*;
 
@DisplayName("PostService")
class PostServiceNestedTest {
 
    private PostService service;
 
    @BeforeEach
    void setUp() {
        service = new PostService(new InMemoryPostRepository(), (to, message) -> { });
    }
 
    @Nested
    @DisplayName("при создании поста")
    class Create {
 
        @Test
        @DisplayName("обрезает пробелы в заголовке")
        void trimsTitle() {
            assertEquals("Привет", service.create("  Привет ", "anna").title());
        }
 
        @Test
        @DisplayName("отклоняет пустой заголовок")
        void rejectsEmptyTitle() {
            assertThrows(IllegalArgumentException.class, () -> service.create(" ", "anna"));
        }
    }
 
    @Nested
    @DisplayName("при публикации поста")
    class Publish {
 
        private Post draft;
 
        @BeforeEach
        void createDraft() {
            draft = service.create("Привет", "anna");
        }
 
        @Test
        @DisplayName("помечает пост опубликованным")
        void marksPublished() {
            assertTrue(service.publish(draft.id()).published());
        }
 
        @Test
        @DisplayName("падает для несуществующего id")
        void unknownId() {
            assertThrows(PostNotFoundException.class, () -> service.publish(99));
        }
    }
}

Перед тестом из Publish JUnit сначала вызывает внешний setUp(), потом внутренний createDraft(). Так каждая группа добавляет свою подготовку поверх общей. Уведомитель здесь — пустая лямбда: эти тесты уведомления не проверяют.

Что вы увидите

В IntelliJ IDEA окно запуска покажет дерево:

PostService
├─ при создании поста
│  ├─ обрезает пробелы в заголовке
│  └─ отклоняет пустой заголовок
└─ при публикации поста
   ├─ помечает пост опубликованным
   └─ падает для несуществующего id

Maven печатает отображаемые имена групп:

[INFO] Running PostService
[INFO] Running при создании поста
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.012 s -- in при создании поста
[INFO] Running при публикации поста
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.010 s -- in при публикации поста
[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.026 s -- in PostService

У самого внешнего класса тестов нет, отсюда Tests run: 0 в последней строке — четыре теста посчитаны в группах. Если в консоли Windows вместо русских букв видны «кракозябры», это кодировка терминала, а не ошибка тестов.

Когда это нужно

Вложенные классы удобны, когда у класса несколько методов, у каждого свой набор случаев, или когда у тестов общее начальное состояние («есть черновик»). Для класса с тремя тестами плоский список проще.

Частая ошибка

Внутренний класс не должен быть static. Статический вложенный класс с @Nested не запускается внутри внешнего, и Surefire не запускает его сам по себе:

WARNING: TestEngine with ID 'junit-jupiter' encountered a non-critical issue during test discovery:
 
(1) [WARNING] @Nested class 'com.example.blog.StaticNestedTest$Create' must not be static. It will only be executed if discovered as a standalone test class. You should remove the annotation or make it non-static to resolve this warning.
...
[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0

Уберите static — тогда класс получит доступ к полям внешнего класса, например к service.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.