К основному содержанию
скилл CX Codex

unit-test-application-events — скилл для Claude Code: тесты событий

Паттерны юнит-тестов для событий Spring: проверка публикации через ApplicationEventPublisher и работа @EventListener без поднятия контекста.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-application-events -a claude-code
Открыть на GitHub

Что делает unit-test-application-events

Скилл даёт готовые шаблоны тестов на JUnit 5 и Mockito для издателей и слушателей Spring ApplicationEvent. Контекст Spring при этом не поднимается: издатель мокается, событие перехватывается через ArgumentCaptor, а поля payload проверяются поштучно. Слушатель тестируется в изоляции — его создают с мок-зависимостями и вызывают метод-обработчик напрямую.

Отдельный блок про асинхронные обработчики: @Async вместе с @EventListener. Тут есть подвох — в примерах скилла ожидание сделано через Thread.sleep(), а в лучших практиках тот же файл советует Awaitility как детерминированный вариант. Если тест на асинхронный слушатель падает, сначала проверьте, не насыщен ли пул исполнителя, и только потом увеличивайте паузу.

Для работы нужны зависимости: spring-boot-starter, JUnit 5, Mockito и AssertJ — примеры подключения есть и для Maven, и для Gradle. Скилл прямо предупреждает: не тестируйте инфраструктуру событий самого Spring, проверяйте свою логику и payload. И помните, что @Async требует @EnableAsync.

Какие задачи решает

  • проверить, что сервис опубликовал событие с нужным payload
  • протестировать @EventListener без запуска Spring-контекста
  • замокать ApplicationEventPublisher в тесте сервиса
  • поймать событие через ArgumentCaptor и сверить поля
  • убедиться, что падение зависимости не роняет слушателя

Примеры запросов агенту

  • напиши юнит-тест на публикацию UserCreatedEvent в UserService
  • проверь через ArgumentCaptor, что событие ушло с правильным пользователем
  • протестируй асинхронный @EventListener с @Async

Как установить unit-test-application-events

1Через Skills CLI (Vercel)

npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-application-events -a claude-code

2Вручную: положить файлы

~/.claude/skills/unit-test-application-events/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/unit-test-application-events/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора93

langchain4j-testing-strategies Помогает тестировать Java-приложения на LangChain4j: от моков ChatModel до RAG-пайплайнов и интеграции с Testcontainers. CX Codex +1 3 700установок aws-sdk-java-v2-s3 Даёт агенту готовые примеры работы с Amazon S3 через AWS SDK for Java 2.x: бакеты, загрузка объектов, multipart, presigned URL. CX Codex +1 3 700установок langchain4j-mcp-server-patterns Скилл помогает проектировать MCP-серверы на Java и подключать их к LangChain4j. Пригодится при вызове инструментов в Spring Boot и фильтрации прав доступа. CX Codex +1 3 700установок langchain4j-vector-stores-configuration Скилл подсказывает, как собрать векторное хранилище для RAG на LangChain4J: подключение, размерность эмбеддингов, фильтры по метаданным. CX Codex +1 3 700установок unit-test-config-properties Скилл добавляет шаблоны модульного тестирования классов @ConfigurationProperties через ApplicationContextRunner в Spring Boot без запуска тяжёлого контекста. CX Codex +1 3 700установок unit-test-caching Паттерны юнит-тестов для аннотаций Spring Cache: проверка попаданий в кэш, вытеснения и ключей SpEL без поднятия полного контекста Spring. CX Codex +1 3 700установок langchain4j-ai-services-patterns Скилл показывает, как собирать декларативные AI Services на LangChain4j: интерфейсы, аннотации, память и вызов инструментов. CX Codex +1 3 700установок qdrant Скилл даёт шаблоны интеграции векторной базы Qdrant в Java и Spring Boot через LangChain4j. Помогает настроить семантический поиск и хранилище эмбеддингов для RAG. CX Codex +1 3 700установок

Похожие

321-frameworks-spring-boot-testing-unit-tests Juan Antonio Breña Moral Скилл ведёт агента по юнит-тестам Spring Boot: Mockito без поднятия контекста, срезы для контроллеров, параметризованные тесты. Пригодится, когда в проекте расплодились @SpringBootTest. CX Codex +1 387установок spring-framework Mindrally Скилл задаёт агенту правила разработки на Spring Framework и Spring Boot: структура слоёв, внедрение зависимостей, тесты, REST. CX Codex +1 731установок 131-java-testing-unit-testing Juan Antonio Breña Moral Скилл помогает писать и рефакторить модульные тесты на Java по стандартам JUnit 5, AssertJ и Mockito. Он находит антипаттерны вроде общего состояния и проверяет граничные условия. CX Codex +1 449установок java Mindrally Скилл задаёт агенту правила Java-разработки: слоистая структура, Spring Boot 3.x, тесты на JUnit и Mockito. Пригодится, когда проект на Java растёт и код надо держать в одном стиле. CX Codex +1 966установок dart-add-unit-test dart-lang Пишет unit-тесты на Dart и Flutter через package:test: раскладывает файлы по test/, добавляет моки и гоняет прогон до зелёного. CX Codex +1 19 200установок java-junit официальный github Пишет, проверяет и исправляет Java-тесты на JUnit, AssertJ и Mockito. Нужен при добавлении, рефакторинге или разборе падений тестов в Maven и Gradle. 11 632установок

Рядом в теме «Тесты и QA»

Всё по теме «Тесты и QA» →

Вопросы о unit-test-application-events

Как установить unit-test-application-events в Claude?

Выполните npx skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-application-events -a claude-code. После этого файл скилла окажется в ~/.claude/skills/unit-test-application-events/SKILL.md.

Что делает unit-test-application-events?

Даёт паттерны юнит-тестов для издателей и слушателей событий Spring: мок ApplicationEventPublisher, захват события через ArgumentCaptor, прямой вызов @EventListener и проверка асинхронной обработки.

Какие зависимости нужны для тестов?

spring-boot-starter, JUnit 5, Mockito и AssertJ. В скилле есть готовые блоки для Maven и Gradle, а для асинхронных слушателей упоминается @EnableAsync.

Исходный текст

SKILL.md · MIT
---
name: unit-test-application-events
description: Provides patterns for unit testing Spring application events. Validates event publishing with ApplicationEventPublisher, tests @EventListener annotation behavior, and verifies async event handling. Use when writing tests for event listeners, mocking application events, or verifying events were published in your Spring Boot services.
allowed-tools: Read, Write, Bash, Glob, Grep
---

# Unit Testing Application Events

## Overview

Provides actionable patterns for testing Spring `ApplicationEvent` publishers and `@EventListener` consumers using JUnit 5 and Mockito — without booting the full Spring context.

## When to Use

- Writing unit tests for event publishers or listeners
- Verifying that an event was published with correct payload
- Testing `@EventListener` method invocation and side effects
- Testing event propagation through multiple listeners
- Validating async event handling (`@Async` + `@EventListener`)
- Mocking `ApplicationEventPublisher` in service tests

## Instructions

1. **Add test dependencies**: `spring-boot-starter`, JUnit 5, Mockito, AssertJ
2. **Mock ApplicationEventPublisher**: use `@Mock` on the publisher field in the service under test
3. **Capture events with ArgumentCaptor**: `ArgumentCaptor.forClass(EventType.class)` to inspect published payload
4. **Verify listener side effects**: invoke listener directly against mocked dependencies
5. **Test async handlers**: use `Thread.sleep()` or Awaitility — then assert the async operation was called
6. **Add validation checkpoints**:
   - After capturing an event, confirm `eventCaptor.getValue()` is not null before asserting fields
   - If the listener is not invoked, verify `publishEvent()` was called with the correct event type
   - If async assertions fail, increase wait time and check the executor pool is not saturated
7. **Cover error scenarios**: assert listeners handle exceptions gracefully

## Examples

### Maven

```xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-core</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.assertj</groupId>
  <artifactId>assertj-core</artifactId>
  <scope>test</scope>
</dependency>
```

### Gradle

```kotlin
dependencies {
  implementation("org.springframework.boot:spring-boot-starter")
  testImplementation("org.junit.jupiter:junit-jupiter")
  testImplementation("org.mockito:mockito-core")
  testImplementation("org.assertj:assertj-core")
}
```

### Custom Event and Publisher Test

```java
public class UserCreatedEvent extends ApplicationEvent {
  private final User user;

  public UserCreatedEvent(Object source, User user) {
    super(source);
    this.user = user;
  }

  public User getUser() { return user; }
}

@Service
public class UserService {
  private final ApplicationEventPublisher eventPublisher;
  private final UserRepository userRepository;

  public UserService(ApplicationEventPublisher eventPublisher, UserRepository userRepository) {
    this.eventPublisher = eventPublisher;
    this.userRepository = userRepository;
  }

  public User createUser(String name, String email) {
    User savedUser = userRepository.save(new User(name, email));
    eventPublisher.publishEvent(new UserCreatedEvent(this, savedUser));
    return savedUser;
  }
}
```

### Unit Test for Event Publishing

```java
@ExtendWith(MockitoExtension.class)
class UserServiceEventTest {

  @Mock
  private ApplicationEventPublisher eventPublisher;

  @Mock
  private UserRepository userRepository;

  @InjectMocks
  private UserService userService;

  @Test
  void shouldPublishUserCreatedEvent() {
    User newUser = new User(1L, "Alice", "[email protected]");
    when(userRepository.save(any(User.class))).thenReturn(newUser);

    ArgumentCaptor<UserCreatedEvent> eventCaptor = ArgumentCaptor.forClass(UserCreatedEvent.class);

    userService.createUser("Alice", "[email protected]");

    verify(eventPublisher).publishEvent(eventCaptor.capture());
    assertThat(eventCaptor.getValue().getUser()).isEqualTo(newUser);
  }
}
```

### Listener Direct Test

```java
@Component
public class UserEventListener {
  private final EmailService emailService;

  public UserEventListener(EmailService emailService) { this.emailService = emailService; }

  @EventListener
  public void onUserCreated(UserCreatedEvent event) {
    emailService.sendWelcomeEmail(event.getUser().getEmail());
  }
}

class UserEventListenerTest {

  @Test
  void shouldSendWelcomeEmailOnUserCreated() {
    EmailService emailService = mock(EmailService.class);
    UserEventListener listener = new UserEventListener(emailService);

    User user = new User(1L, "Alice", "[email protected]");
    listener.onUserCreated(new UserCreatedEvent(this, user));

    verify(emailService).sendWelcomeEmail("[email protected]");
  }

  @Test
  void shouldNotThrowWhenEmailServiceFails() {
    EmailService emailService = mock(EmailService.class);
    doThrow(new RuntimeException("down")).when(emailService).sendWelcomeEmail(any());

    UserEventListener listener = new UserEventListener(emailService);
    User user = new User(1L, "Alice", "[email protected]");

    assertThatCode(() -> listener.onUserCreated(new UserCreatedEvent(this, user)))
      .doesNotThrowAnyException();
  }
}
```

### Async Listener Test

```java
@Component
public class AsyncEventListener {
  private final SlowService slowService;

  @EventListener
  @Async
  public void onUserCreatedAsync(UserCreatedEvent event) {
    slowService.processUser(event.getUser());
  }
}

class AsyncEventListenerTest {

  @Test
  void shouldProcessEventAsynchronously() throws Exception {
    SlowService slowService = mock(SlowService.class);
    AsyncEventListener listener = new AsyncEventListener(slowService);

    User user = new User(1L, "Alice", "[email protected]");
    listener.onUserCreatedAsync(new UserCreatedEvent(this, user));

    Thread.sleep(200); // checkpoint: allow async executor to run
    verify(slowService).processUser(user);
  }
}
```

## Best Practices

- Mock `ApplicationEventPublisher` — never let it post to a real context in unit tests
- Capture events with `ArgumentCaptor` and assert field-level equality, not just type
- Test listeners in isolation: construct them with mocked dependencies and call the handler method directly
- Cover error paths: listeners must not propagate exceptions to publishers
- Async listeners: prefer Awaitility over `Thread.sleep()` for deterministic waits
- Keep events immutable and serializable — test both if events cross JVM boundaries

## Constraints and Warnings

- **Do not test Spring's own event infrastructure** — focus on your business logic and event payload
- **`@Async` requires `@EnableAsync`** — tests using Thread.sleep may still pass even if the async proxy is not wired in the test; use a mock verify instead
- **Spring does not guarantee listener order** — do not write tests that depend on execution sequence unless you add `@Order`
- **Avoid `Thread.sleep()` in CI environments** — it makes tests flaky under load; replace with Awaitility `.atMost()` blocks
- **Events crossing JVM boundaries need serialization tests** — null fields in remote listeners often mean missing `Serializable`

## References

- [Spring ApplicationEvent Javadoc](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationEvent.html)
- [ApplicationEventPublisher Javadoc](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationEventPublisher.html)
- [`@EventListener` Javadoc](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/event/EventListener.html)

© giuseppe-trisciuoglio · MIT · giuseppe-trisciuoglio/developer-kit

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой