15
15
*/
16
16
package org .springframework .modulith .test ;
17
17
18
+ import static org .assertj .core .api .Assertions .*;
19
+
18
20
import java .time .Duration ;
21
+ import java .util .Optional ;
19
22
import java .util .concurrent .Callable ;
20
23
import java .util .function .BiConsumer ;
21
24
import java .util .function .BiFunction ;
40
43
import org .springframework .transaction .support .TransactionTemplate ;
41
44
import org .springframework .util .Assert ;
42
45
43
- import com .tngtech .archunit .thirdparty .com .google .common .base .Optional ;
44
-
45
46
/**
46
47
* A DSL to define integration testing scenarios for application modules. A {@link Scenario} starts with a stimulus on
47
48
* the system, usually a component invocation (see {@link #stimulate(Function)} or event publication (see
@@ -338,13 +339,16 @@ public <S> StateChangeResult<S> forStateChange(Supplier<S> supplier, Predicate<?
338
339
}
339
340
340
341
/**
342
+ * Expects an event of the given type to arrive. Use API on the returned {@link EventResult} to specify more
343
+ * detailed expectations and conclude those with a call a flavor of {@link EventResult#toArrive()}.
344
+ *
341
345
* @param <E> the type of the event.
342
346
* @param type must not be {@literal null}.
343
347
* @return will never be {@literal null}.
344
348
* @see #forEventOfType(Class)
345
349
*/
346
350
public <E > EventResult <E > andWaitForEventOfType (Class <E > type ) {
347
- return new EventResult <E >(type , Function .identity ());
351
+ return new EventResult <E >(type , Function .identity (), null );
348
352
}
349
353
350
354
/**
@@ -402,6 +406,11 @@ private <S> ExecutionResult<S, T> awaitInternal(Consumer<T> verifications, Calla
402
406
403
407
private record ExecutionResult <S , T >(S first , T second ) {}
404
408
409
+ /**
410
+ * The result of an expected state change.
411
+ *
412
+ * @author Oliver Drotbohm
413
+ */
405
414
public class StateChangeResult <S > {
406
415
407
416
private ExecutionResult <S , T > result ;
@@ -445,25 +454,48 @@ public void andVerifyEvents(Consumer<AssertablePublishedEvents> events) {
445
454
446
455
events .accept (Scenario .this .events );
447
456
}
457
+
458
+ /**
459
+ * Expects an event of the given type to arrive eventually. Use API on the returned {@link EventResult} to specify
460
+ * more detailed expectations and conclude those with a call a flavor of {@link EventResult#toArrive()}.
461
+ *
462
+ * @param <E> the type of the event
463
+ * @param eventType must not be {@literal null}.
464
+ * @return will never be {@literal null}.
465
+ */
466
+ public <E > EventResult <E > andExpect (Class <E > eventType ) {
467
+ return new EventResult <>(eventType , Function .identity (), result );
468
+ }
448
469
}
449
470
471
+ /**
472
+ * The result of an expected event publication.
473
+ *
474
+ * @author Oliver Drotbohm
475
+ */
450
476
public class EventResult <E > {
451
477
478
+ private static final String EXPECTED_EVENT = "Expected an event of type %s (potentially further constrained using matching clauses above) to be published but couldn't find one in %s!" ;
479
+
452
480
private final Class <E > type ;
453
481
private final Function <TypedPublishedEvents <E >, TypedPublishedEvents <E >> filter ;
482
+ private final ExecutionResult <?, T > previousResult ;
454
483
455
484
/**
456
485
* Creates a new {@link EventResult} for the given type and filter.
457
486
*
458
487
* @param type must not be {@literal null}.
459
488
* @param filtered must not be {@literal null}.
489
+ * @param previousResult a potentially previously calculated result.
460
490
*/
461
- EventResult (Class <E > type , Function <TypedPublishedEvents <E >, TypedPublishedEvents <E >> filtered ) {
491
+ EventResult (Class <E > type , Function <TypedPublishedEvents <E >, TypedPublishedEvents <E >> filtered ,
492
+ ExecutionResult <?, T > previousResult ) {
462
493
463
494
Assert .notNull (type , "Event type must not be null!" );
464
495
465
496
this .type = type ;
466
497
this .filter = filtered ;
498
+ this .previousResult = previousResult ;
467
499
}
468
500
469
501
/**
@@ -476,7 +508,7 @@ public EventResult<E> matching(Predicate<? super E> filter) {
476
508
477
509
Assert .notNull (filter , "Filter must not be null!" );
478
510
479
- return new EventResult <E >(type , createOrAdd (it -> it .matching (filter )));
511
+ return new EventResult <E >(type , createOrAdd (it -> it .matching (filter )), previousResult );
480
512
}
481
513
482
514
/**
@@ -489,7 +521,7 @@ public EventResult<E> matching(Predicate<? super E> filter) {
489
521
* @return will never be {@literal null}.
490
522
*/
491
523
public <S > EventResult <E > matchingMapped (Function <E , S > extractor , Predicate <? super S > filter ) {
492
- return new EventResult <E >(type , createOrAdd (it -> it .matching (extractor , filter )));
524
+ return new EventResult <E >(type , createOrAdd (it -> it .matching (extractor , filter )), previousResult );
493
525
}
494
526
495
527
/**
@@ -501,7 +533,7 @@ public <S> EventResult<E> matchingMapped(Function<E, S> extractor, Predicate<? s
501
533
* @return will never be {@literal null}.
502
534
*/
503
535
public <S > EventResult <E > matchingMappedValue (Function <E , S > extractor , @ Nullable S value ) {
504
- return new EventResult <E >(type , createOrAdd (it -> it .matching (extractor , value )));
536
+ return new EventResult <E >(type , createOrAdd (it -> it .matching (extractor , value )), previousResult );
505
537
}
506
538
507
539
/**
@@ -584,7 +616,18 @@ private PublishedEventAssert<? super E> getAssertedEvent() {
584
616
}
585
617
586
618
private void toArriveAndVerifyInternal (Consumer <T > verifications ) {
587
- awaitInternal (verifications , () -> getFilteredEvents (), it -> it .eventOfTypeWasPublished (type ));
619
+
620
+ if (previousResult != null ) {
621
+
622
+ assertThat (getFilteredEvents ().eventOfTypeWasPublished (type ))
623
+ .overridingErrorMessage (EXPECTED_EVENT , type , events )
624
+ .isTrue ();
625
+
626
+ verifications .accept (previousResult .second ());
627
+
628
+ } else {
629
+ awaitInternal (verifications , () -> getFilteredEvents (), it -> it .eventOfTypeWasPublished (type ));
630
+ }
588
631
}
589
632
}
590
633
}
0 commit comments