unit-test-application-events by giuseppe-trisciuoglio/developer-kit
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-application-events本技能提供了使用 JUnit 5 测试 Spring ApplicationEvent 发布器和事件监听器的全面模式。它涵盖了测试事件发布、监听器执行、事件传播以及同步和异步事件处理,无需完整的 Spring 上下文启动。
在以下情况下使用此技能:
@EventListener 方法调用@Mock 来模拟事件发布器ArgumentCaptor.forClass(EventType.class) 验证其内容@Async 监听器,使用 Thread.sleep() 或 Awaitility 等待完成广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
<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>
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.mockito:mockito-core")
testImplementation("org.assertj:assertj-core")
}
// 自定义应用程序事件
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 user = new User(name, email);
User savedUser = userRepository.save(user);
eventPublisher.publishEvent(new UserCreatedEvent(this, savedUser));
return savedUser;
}
}
// 单元测试
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
@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", "alice@example.com");
when(userRepository.save(any(User.class))).thenReturn(newUser);
ArgumentCaptor<UserCreatedEvent> eventCaptor = ArgumentCaptor.forClass(UserCreatedEvent.class);
userService.createUser("Alice", "alice@example.com");
verify(eventPublisher).publishEvent(eventCaptor.capture());
UserCreatedEvent capturedEvent = eventCaptor.getValue();
assertThat(capturedEvent.getUser()).isEqualTo(newUser);
}
}
@EventListener 注解// 事件监听器
@Component
public class UserEventListener {
private final EmailService emailService;
public UserEventListener(EmailService emailService) {
this.emailService = emailService;
}
@EventListener
public void onUserCreated(UserCreatedEvent event) {
User user = event.getUser();
emailService.sendWelcomeEmail(user.getEmail());
}
}
// 监听器的单元测试
class UserEventListenerTest {
@Test
void shouldSendWelcomeEmailWhenUserCreated() {
EmailService emailService = mock(EmailService.class);
UserEventListener listener = new UserEventListener(emailService);
User newUser = new User(1L, "Alice", "alice@example.com");
UserCreatedEvent event = new UserCreatedEvent(this, newUser);
listener.onUserCreated(event);
verify(emailService).sendWelcomeEmail("alice@example.com");
}
@Test
void shouldNotThrowExceptionWhenEmailServiceFails() {
EmailService emailService = mock(EmailService.class);
doThrow(new RuntimeException("Email service down"))
.when(emailService).sendWelcomeEmail(any());
UserEventListener listener = new UserEventListener(emailService);
User newUser = new User(1L, "Alice", "alice@example.com");
UserCreatedEvent event = new UserCreatedEvent(this, newUser);
// 应优雅地处理异常
assertThatCode(() -> listener.onUserCreated(event))
.doesNotThrowAnyException();
}
}
class UserCreatedEvent extends ApplicationEvent {
private final User user;
private final List<String> notifications = new ArrayList<>();
public UserCreatedEvent(Object source, User user) {
super(source);
this.user = user;
}
public void addNotification(String notification) {
notifications.add(notification);
}
public List<String> getNotifications() {
return notifications;
}
}
class MultiListenerTest {
@Test
void shouldNotifyMultipleListenersSequentially() {
EmailService emailService = mock(EmailService.class);
NotificationService notificationService = mock(NotificationService.class);
AnalyticsService analyticsService = mock(AnalyticsService.class);
UserEventListener emailListener = new UserEventListener(emailService);
UserEventListener notificationListener = new UserEventListener(notificationService);
UserEventListener analyticsListener = new UserEventListener(analyticsService);
User user = new User(1L, "Alice", "alice@example.com");
UserCreatedEvent event = new UserCreatedEvent(this, user);
emailListener.onUserCreated(event);
notificationListener.onUserCreated(event);
analyticsListener.onUserCreated(event);
verify(emailService).send(any());
verify(notificationService).notify(any());
verify(analyticsService).track(any());
}
}
@EventListener@Component
public class ConditionalEventListener {
@EventListener(condition = "#event.user.age > 18")
public void onAdultUserCreated(UserCreatedEvent event) {
// 处理成年用户
}
}
class ConditionalListenerTest {
@Test
void shouldProcessEventWhenConditionMatches() {
// 匹配条件的测试逻辑
}
@Test
void shouldSkipEventWhenConditionDoesNotMatch() {
// 不匹配条件的测试逻辑
}
}
@Async 与 @EventListener@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", "alice@example.com");
UserCreatedEvent event = new UserCreatedEvent(this, user);
listener.onUserCreatedAsync(event);
// 事件异步处理
Thread.sleep(100); // 等待异步完成
verify(slowService).processUser(user);
}
}
@Order 注解时,Spring 不保证监听器执行顺序事件未被捕获:验证 ArgumentCaptor 类型是否与事件类匹配。
监听器未被调用:确保事件实际发布且监听器已注册。
异步监听器时序问题:使用 Thread.sleep() 或 Awaitility 等待完成。
每周安装数
332
仓库
GitHub 星标数
173
首次出现
2026年2月3日
安全审计
安装于
claude-code265
gemini-cli251
opencode250
cursor247
codex245
github-copilot231
This skill provides comprehensive patterns for testing Spring ApplicationEvent publishers and event listeners using JUnit 5. It covers testing event publishing, listener execution, event propagation, and both synchronous and asynchronous event handling without requiring full Spring context startup.
Use this skill when:
@EventListener method invocation@Mock to mock the event publisherArgumentCaptor.forClass(EventType.class)@Async listeners, use Thread.sleep() or Awaitility to wait for completion<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>
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 application event
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 that publishes events
@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 user = new User(name, email);
User savedUser = userRepository.save(user);
eventPublisher.publishEvent(new UserCreatedEvent(this, savedUser));
return savedUser;
}
}
// Unit test
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
@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", "alice@example.com");
when(userRepository.save(any(User.class))).thenReturn(newUser);
ArgumentCaptor<UserCreatedEvent> eventCaptor = ArgumentCaptor.forClass(UserCreatedEvent.class);
userService.createUser("Alice", "alice@example.com");
verify(eventPublisher).publishEvent(eventCaptor.capture());
UserCreatedEvent capturedEvent = eventCaptor.getValue();
assertThat(capturedEvent.getUser()).isEqualTo(newUser);
}
}
@EventListener Annotation// Event listener
@Component
public class UserEventListener {
private final EmailService emailService;
public UserEventListener(EmailService emailService) {
this.emailService = emailService;
}
@EventListener
public void onUserCreated(UserCreatedEvent event) {
User user = event.getUser();
emailService.sendWelcomeEmail(user.getEmail());
}
}
// Unit test for listener
class UserEventListenerTest {
@Test
void shouldSendWelcomeEmailWhenUserCreated() {
EmailService emailService = mock(EmailService.class);
UserEventListener listener = new UserEventListener(emailService);
User newUser = new User(1L, "Alice", "alice@example.com");
UserCreatedEvent event = new UserCreatedEvent(this, newUser);
listener.onUserCreated(event);
verify(emailService).sendWelcomeEmail("alice@example.com");
}
@Test
void shouldNotThrowExceptionWhenEmailServiceFails() {
EmailService emailService = mock(EmailService.class);
doThrow(new RuntimeException("Email service down"))
.when(emailService).sendWelcomeEmail(any());
UserEventListener listener = new UserEventListener(emailService);
User newUser = new User(1L, "Alice", "alice@example.com");
UserCreatedEvent event = new UserCreatedEvent(this, newUser);
// Should handle exception gracefully
assertThatCode(() -> listener.onUserCreated(event))
.doesNotThrowAnyException();
}
}
class UserCreatedEvent extends ApplicationEvent {
private final User user;
private final List<String> notifications = new ArrayList<>();
public UserCreatedEvent(Object source, User user) {
super(source);
this.user = user;
}
public void addNotification(String notification) {
notifications.add(notification);
}
public List<String> getNotifications() {
return notifications;
}
}
class MultiListenerTest {
@Test
void shouldNotifyMultipleListenersSequentially() {
EmailService emailService = mock(EmailService.class);
NotificationService notificationService = mock(NotificationService.class);
AnalyticsService analyticsService = mock(AnalyticsService.class);
UserEventListener emailListener = new UserEventListener(emailService);
UserEventListener notificationListener = new UserEventListener(notificationService);
UserEventListener analyticsListener = new UserEventListener(analyticsService);
User user = new User(1L, "Alice", "alice@example.com");
UserCreatedEvent event = new UserCreatedEvent(this, user);
emailListener.onUserCreated(event);
notificationListener.onUserCreated(event);
analyticsListener.onUserCreated(event);
verify(emailService).send(any());
verify(notificationService).notify(any());
verify(analyticsService).track(any());
}
}
@EventListener with Condition@Component
public class ConditionalEventListener {
@EventListener(condition = "#event.user.age > 18")
public void onAdultUserCreated(UserCreatedEvent event) {
// Handle adult user
}
}
class ConditionalListenerTest {
@Test
void shouldProcessEventWhenConditionMatches() {
// Test logic for matching condition
}
@Test
void shouldSkipEventWhenConditionDoesNotMatch() {
// Test logic for non-matching condition
}
}
@Async with @EventListener@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", "alice@example.com");
UserCreatedEvent event = new UserCreatedEvent(this, user);
listener.onUserCreatedAsync(event);
// Event processed asynchronously
Thread.sleep(100); // Wait for async completion
verify(slowService).processUser(user);
}
}
@Order annotationEvent not being captured : Verify ArgumentCaptor type matches event class.
Listener not invoked : Ensure event is actually published and listener is registered.
Async listener timing issues : Use Thread.sleep() or Awaitility to wait for completion.
Weekly Installs
332
Repository
GitHub Stars
173
First Seen
Feb 3, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
claude-code265
gemini-cli251
opencode250
cursor247
codex245
github-copilot231
Spring Boot工程师技能指南:微服务架构、安全加固与云原生开发实战
2,800 周安装