Add awaitility module: testing async @Async/@Scheduled/@KafkaListener code without Thread.sleep
Covers replacing Thread.sleep with await() across a void @Async method, an @EmbeddedKafka-backed @KafkaListener, and an already-running @Scheduled job; verifies Awaitility 4.3.0's real defaults (10s timeout, 100ms poll interval) and its default-uncaught-exception-handler swap directly against the jar; and documents a real pom.xml trap where Boot 4.1.1 split Kafka's autoconfiguration (spring-boot-kafka) out of spring-kafka itself, which silently leaves @KafkaListener beans with no running container.
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
[INFO] Running com.ankurm.awaitility.KafkaListenerAwaitTest
|
||||
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.012 s -- in com.ankurm.awaitility.KafkaListenerAwaitTest
|
||||
[INFO] Running com.ankurm.awaitility.ScheduledJobAwaitTest
|
||||
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.841 s -- in com.ankurm.awaitility.ScheduledJobAwaitTest
|
||||
[INFO] Running com.ankurm.awaitility.UncaughtExceptionHandlerSwapTest
|
||||
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.115 s -- in com.ankurm.awaitility.UncaughtExceptionHandlerSwapTest
|
||||
[INFO] Running com.ankurm.awaitility.AwaitAsyncConfirmationTest
|
||||
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.654 s -- in com.ankurm.awaitility.AwaitAsyncConfirmationTest
|
||||
[INFO] Running com.ankurm.awaitility.IgnoreExceptionsTest
|
||||
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.636 s -- in com.ankurm.awaitility.IgnoreExceptionsTest
|
||||
[INFO] Running com.ankurm.awaitility.DefaultTimingTest
|
||||
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.70 s -- in com.ankurm.awaitility.DefaultTimingTest
|
||||
[INFO] Tests run: 11, Failures: 0, Errors: 0, Skipped: 0
|
||||
[INFO] BUILD SUCCESS
|
||||
@@ -0,0 +1,9 @@
|
||||
== Thread.sleep(100) against a confirmation that actually takes 220ms (real failure, test since removed) ==
|
||||
|
||||
$ mvn -B -Dtest=SleepGuessesWrongTest test
|
||||
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 3.568 s <<< FAILURE! -- in com.ankurm.awaitility.SleepGuessesWrongTest
|
||||
com.ankurm.awaitility.SleepGuessesWrongTest.confirmationArrivesWithin100ms -- Time elapsed: 1.084 s <<< FAILURE!
|
||||
java.lang.AssertionError:
|
||||
|
||||
Expecting actual not to be null
|
||||
at com.ankurm.awaitility.SleepGuessesWrongTest.confirmationArrivesWithin100ms(SleepGuessesWrongTest.java:27)
|
||||
@@ -0,0 +1,6 @@
|
||||
== await().untilAsserted() against a 220ms-delayed void @Async method ==
|
||||
|
||||
mailbox.confirmationFor("ORD-CAPTURE") immediately before sendConfirmation(): null
|
||||
await().atMost(2s).untilAsserted(...) returned after approximately: 307ms
|
||||
mailbox.confirmationFor("ORD-CAPTURE") once await() returns: order ORD-CAPTURE confirmed
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
== await().until(() -> false) with no atMost(...) override ==
|
||||
|
||||
elapsed before ConditionTimeoutException: 10057ms (expected: ~10,000ms)
|
||||
@@ -0,0 +1,6 @@
|
||||
== await().atMost(550ms).until(...) with no pollInterval(...) override ==
|
||||
|
||||
poll timestamps (ms since start): [107, 207, 308, 409, 509]
|
||||
gaps between consecutive polls (ms): [100, 101, 101, 100]
|
||||
average gap: 100.5ms (expected: ~100ms)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
== await() for an @KafkaListener to consume a record a KafkaTemplate just sent, against an @EmbeddedKafka broker ==
|
||||
|
||||
listener.received() immediately after send() returned: (not checked -- that is the point)
|
||||
listener.received() once await() returned: [order-77-created]
|
||||
approximate elapsed time: 378ms
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
== await() for 3 more @Scheduled(fixedRate = 150) executions, regardless of how long the job had already been running ==
|
||||
|
||||
runCount() when the test started: 1
|
||||
target (baseline + 3): 4
|
||||
runCount() once await() returned: 4
|
||||
approximate elapsed time: 510ms
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
== Thread.getDefaultUncaughtExceptionHandler() before, during and after one await() call ==
|
||||
|
||||
handler during await() is the one this test installed: false (expected: false)
|
||||
handler during await() is Awaitility's own (class org.awaitility.core.CallableCondition$1)
|
||||
handler after await() is the one this test installed: true (expected: true)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
== await().untilAsserted(...) with no ignoreExceptionsInstanceOf(...), against a resource that throws for its first 300ms (real failure, test since removed) ==
|
||||
|
||||
$ mvn -B -Dtest=ExceptionPropagatesImmediatelyTest test
|
||||
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.940 s <<< FAILURE! -- in com.ankurm.awaitility.ExceptionPropagatesImmediatelyTest
|
||||
com.ankurm.awaitility.ExceptionPropagatesImmediatelyTest.failsOnTheFirstPollInsteadOfWaiting -- Time elapsed: 0.946 s <<< ERROR!
|
||||
java.lang.IllegalStateException: resource is still warming up
|
||||
at com.ankurm.awaitility.FlakyStartupResource.value(FlakyStartupResource.java:26)
|
||||
at com.ankurm.awaitility.ExceptionPropagatesImmediatelyTest.lambda$failsOnTheFirstPollInsteadOfWaiting$0(ExceptionPropagatesImmediatelyTest.java:28)
|
||||
|
||||
Note "Errors: 1", not a ConditionTimeoutException after the full atMost(1s) window: the exception
|
||||
from the very first poll propagated straight out instead of being retried.
|
||||
@@ -0,0 +1,3 @@
|
||||
== await().ignoreExceptionsInstanceOf(IllegalStateException.class) against a resource that throws for its first 300ms ==
|
||||
|
||||
approximate elapsed time before resource.value() finally returned "ready": 302ms (expected: a little over 300ms)
|
||||
@@ -0,0 +1,23 @@
|
||||
== Real failure with org.springframework.kafka:spring-kafka as a direct dependency instead of org.springframework.boot:spring-boot-starter-kafka (pom.xml since fixed) ==
|
||||
|
||||
$ mvn -B test
|
||||
[ERROR] Errors:
|
||||
[ERROR] KafkaListenerAwaitTest.messageSentIsEventuallyConsumed:49 ? ConditionTimeout Assertion condition defined as a Lambda expression in com.ankurm.awaitility.KafkaListenerAwaitTest
|
||||
Expecting ConcurrentLinkedQueue:
|
||||
[]
|
||||
to contain:
|
||||
["order-42-created"]
|
||||
but could not find the following element(s):
|
||||
["order-42-created"]
|
||||
within 5 seconds.
|
||||
[INFO]
|
||||
[ERROR] Tests run: 11, Failures: 0, Errors: 2, Skipped: 0
|
||||
|
||||
No exception at context startup, and the application context came up without a single warning.
|
||||
Autowiring KafkaListenerEndpointRegistry directly in a scratch test and calling
|
||||
getListenerContainers() on it confirmed the real cause: the bean did not exist. Neither did
|
||||
KafkaTemplate, ConsumerFactory, or kafkaListenerContainerFactory. Grepping the full log for the
|
||||
Spring Kafka logger prefix also confirmed it:
|
||||
|
||||
$ grep -c "o\.s\.k\." full-test-run.log
|
||||
0
|
||||
@@ -0,0 +1,38 @@
|
||||
== Confirming the Boot 4.1 Kafka module split directly against Maven Central's real POMs ==
|
||||
|
||||
$ curl -sS https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-dependencies/4.1.1/spring-boot-dependencies-4.1.1.pom -o spring-boot-dependencies-4.1.1.pom
|
||||
$ grep -B3 '<artifactId>spring-boot-kafka</artifactId>' spring-boot-dependencies-4.1.1.pom
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-kafka</artifactId>
|
||||
|
||||
$ curl -sS https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-starter-kafka/4.1.1/spring-boot-starter-kafka-4.1.1.pom | grep -A2 '<artifactId>'
|
||||
<artifactId>spring-boot-starter-kafka</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<name>spring-boot-starter-kafka</name>
|
||||
--
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<scope>compile</scope>
|
||||
--
|
||||
<artifactId>spring-boot-kafka</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<scope>compile</scope>
|
||||
|
||||
$ curl -sS https://repo1.maven.org/maven2/org/springframework/boot/spring-boot-kafka/4.1.1/spring-boot-kafka-4.1.1.pom | grep -A2 '<artifactId>'
|
||||
<artifactId>spring-boot-kafka</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<name>spring-boot-kafka</name>
|
||||
--
|
||||
<artifactId>spring-boot</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<scope>compile</scope>
|
||||
--
|
||||
<artifactId>spring-boot-transaction</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<scope>compile</scope>
|
||||
--
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<scope>compile</scope>
|
||||
Reference in New Issue
Block a user