Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
giuseppe-trisciuoglio avatar

Unit Test Scheduled Async

  • 1.6k installs
  • 311 repo stars
  • Updated June 22, 2026
  • giuseppe-trisciuoglio/developer-kit

Patterns and practices for unit testing Spring @Scheduled and @Async methods using JUnit 5, CompletableFuture, Awaitility, and Mockito.

About

Provides JUnit 5 patterns for testing Spring @Scheduled and @Async methods using CompletableFuture, Awaitility, and Mockito. Developers use this when testing background tasks, cron jobs, and async error handling in isolation. Key workflows include calling @Async/@Scheduled methods directly (bypassing Spring proxies), mocking dependencies with Mockito, waiting for completion with CompletableFuture.get() or Awaitility, and validating execution counts and exception propagation. Covers race conditions, timeout management, and testing CompletableFuture result values before verifying mock interactions.

  • Call @Async and @Scheduled methods directly in tests; Spring annotations are ignored in unit tests
  • Use CompletableFuture.get(timeout, unit) with timeout to prevent test hangs
  • Employ Awaitility with atMost() and pollInterval() for race conditions and shared mutable state
  • Mock all dependencies with @Mock and @InjectMocks; test exception propagation via ExecutionException.getCause()
  • Verify execution counts and mock interactions only after async completion and value assertions

Unit Test Scheduled Async by the numbers

  • 1,635 all-time installs (skills.sh)
  • +55 installs in the week ending Jul 28, 2026 (Skillselion tracking)
  • Ranked #443 of 2,184 Testing & QA skills by installs in the Skillselion catalog
  • Security screen: MEDIUM risk (skills.sh audit)
  • Data as of Jul 28, 2026 (Skillselion catalog sync)
At a glance

unit-test-scheduled-async capabilities & compatibility

Capabilities
direct method invocation bypassing spring async · timeout safe waiting with completablefuture.get( · race condition handling with awaitility polling · mock dependency setup and verification · exception propagation and executionexception unw · execution count validation for repeated task inv
Use cases
testing · debugging
Platforms
macOS · Windows · Linux
IDEs
vscode · intellij · jetbrains
Runs
Runs locally
Pricing
Free
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-scheduled-async

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1.6k
repo stars311
Security audit3 / 3 scanners passed
Last updatedJune 22, 2026
Repositorygiuseppe-trisciuoglio/developer-kit

What it does

Unit test Spring @Scheduled and @Async methods with JUnit 5, mocking, and Awaitility without waiting for real scheduling intervals.

Who is it for?

Backend developers testing Spring scheduled and async task logic, cron expression validation, thread pool behavior, and async error handling.

Skip if: Integration testing actual Spring task scheduling, end-to-end async workflows with real external services, or timing-sensitive performance benchmarks.

When should I use this skill?

Writing unit tests for Spring @Scheduled methods, @Async methods returning CompletableFuture, background task logic, or async exception handling.

What you get

Developers can confidently unit test @Scheduled cron jobs, @Async background tasks, and CompletableFuture chains with proper mocking, timeout management, and exception validation.

  • Unit test patterns for @Scheduled and @Async methods
  • CompletableFuture timeout and exception handling examples
  • Awaitility usage patterns for race conditions

By the numbers

  • Covers timeout management with CompletableFuture.get(timeout, unit)
  • Includes Awaitility polling patterns with atMost() and pollInterval() durations
  • Provides exception handling via ExecutionException.getCause() validation

Files

SKILL.mdMarkdownGitHub ↗

Unit Testing @Scheduled and @Async Methods

Overview

Patterns for unit testing Spring @Scheduled and @Async methods with JUnit 5. Test CompletableFuture results, use Awaitility for race conditions, mock scheduled task execution, and validate error handling — without waiting for real scheduling intervals.

When to Use

  • Testing @Scheduled method logic
  • Testing @Async method behavior
  • Verifying CompletableFuture results
  • Testing async error handling
  • Testing cron expression logic without waiting for actual scheduling
  • Validating thread pool behavior and execution counts
  • Testing background task logic in isolation

Instructions

1. Call `@Async` methods directly — bypass Spring's async proxy; the annotation is irrelevant in unit tests 2. Mock dependencies with @Mock and @InjectMocks (Mockito) 3. Wait for completion — use CompletableFuture.get(timeout, unit) or await().atMost(...).untilAsserted(...) 4. Call `@Scheduled` methods directly — do not wait for cron/fixedRate; the annotation is ignored in unit tests 5. Test exception paths — verify ExecutionException wrapping on CompletableFuture.get()

Validation checkpoints:

  • After CompletableFuture.get(), assert the returned value before verifying mock interactions
  • If ExecutionException is thrown, check .getCause() to identify the root exception
  • If Awaitility times out, increase atMost() duration or reduce pollInterval() until the condition is reachable
  • After multiple task invocations, assert execution counts before verify() calls

Examples

Key patterns — complete examples in references/examples.md:

// @Async: call directly, wait with CompletableFuture.get(timeout, unit)
@Service
class EmailService {
  @Async
  public CompletableFuture<Boolean> sendEmailAsync(String to) {
    return CompletableFuture.supplyAsync(() -> true);
  }
}
@Test
void shouldReturnCompletedFuture() throws Exception {
  EmailService service = new EmailService();
  Boolean result = service.sendEmailAsync("test@example.com").get(5, TimeUnit.SECONDS);
  assertThat(result).isTrue();
}

// @Scheduled: call directly, mock the repository
@Component
class DataRefreshTask {
  @InjectMocks private DataRepository dataRepository;
  @Scheduled(fixedDelay = 60000) public void refreshCache() { /* ... */ }
}
@Test
void shouldRefreshCache() {
  when(dataRepository.findAll()).thenReturn(List.of(new Data(1L, "item1")));
  dataRefreshTask.refreshCache();
  verify(dataRepository).findAll();
}

// Awaitility: use for race conditions with shared mutable state
@Test
void shouldProcessAllItems() {
  BackgroundWorker worker = new BackgroundWorker();
  worker.processItems(List.of("item1", "item2", "item3"));
  Awaitility.await()
    .atMost(Duration.ofSeconds(5))
    .pollInterval(Duration.ofMillis(100))
    .untilAsserted(() -> assertThat(worker.getProcessedCount()).isEqualTo(3));
}

// Mocked dependencies with exception handling
@Test
void shouldHandleAsyncExceptionGracefully() {
  doThrow(new RuntimeException("Email failed")).when(emailService).send(any());
  CompletableFuture<String> result = service.notifyUserAsync("user123");
  assertThatThrownBy(result::get)
    .isInstanceOf(ExecutionException.class)
    .hasCauseInstanceOf(RuntimeException.class);
}

Full Maven/Gradle dependencies, additional test classes, and execution count patterns: see references/examples.md.

Best Practices

  • Always set a timeout on CompletableFuture.get() to prevent hanging tests
  • Mock all dependencies — never call real external services in unit tests
  • Use Awaitility only for race conditions; prefer direct calls for simple async methods
  • Test @Scheduled logic directly — the annotation is ignored in unit tests
  • Assert values before verifying mock interactions; verify after async completion

Common Pitfalls

  • Relying on Spring's async executor instead of calling methods directly
  • Missing timeout on CompletableFuture.get()
  • Forgetting to test exception propagation in async methods
  • Not mocking dependencies that async methods invoke internally
  • Waiting for actual cron/fixedRate timing instead of testing logic in isolation

Constraints and Warnings

  • `@Async` self-invocation: calling @Async from another method in the same class executes synchronously — the Spring proxy is bypassed
  • Thread pool ordering: ThreadPoolTaskScheduler does not guarantee execution order
  • CompletableFuture chaining: exceptions in intermediate stages can be silently lost — test each stage
  • Awaitility timeout: always set a reasonable atMost(); infinite waits hang the test suite
  • No actual scheduling: @Scheduled is ignored in unit tests — call methods directly

References

Related skills

FAQ

Do I need to wait for actual cron/fixedRate timing in unit tests?

No. Call @Scheduled methods directly in tests; the annotation is ignored. Test the method logic in isolation without waiting for real scheduling intervals.

What timeout should I use for CompletableFuture.get()?

Always set a reasonable timeout (e.g., 5 seconds) to prevent test hangs. If Awaitility times out, increase atMost() duration or reduce pollInterval() until the condition is reachable.

How do I test exception handling in @Async methods?

Verify ExecutionException is thrown on CompletableFuture.get() and check .getCause() to identify the root exception. Mock dependencies to simulate failure paths.

Is Unit Test Scheduled Async safe to install?

skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.

Testing & QAtestingbackend

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.