1717package org .springframework .core .retry ;
1818
1919import java .time .Duration ;
20+ import java .util .concurrent .atomic .AtomicInteger ;
2021
22+ import org .junit .jupiter .api .BeforeEach ;
2123import org .junit .jupiter .api .Test ;
2224
2325import org .springframework .util .backoff .FixedBackOff ;
@@ -36,95 +38,99 @@ class RetryTemplateTests {
3638
3739 private final RetryTemplate retryTemplate = new RetryTemplate ();
3840
39- @ Test
40- void retryWithSuccess () throws Exception {
41- Retryable <String > retryable = new Retryable <>() {
4241
43- int failure ;
42+ @ BeforeEach
43+ void configureTemplate () {
44+ this .retryTemplate .setBackOffPolicy (new FixedBackOff (Duration .ofMillis (10 )));
45+ }
4446
45- @ Override
46- public String execute () throws Exception {
47- if ( failure ++ < 2 ) {
48- throw new Exception ( "Error while invoking greeting service" );
49- }
50- return "hello world " ;
51- }
47+ @ Test
48+ void retryWithImmediateSuccess () throws Exception {
49+ AtomicInteger invocationCount = new AtomicInteger ();
50+ Retryable < String > retryable = () -> {
51+ invocationCount . incrementAndGet ();
52+ return "always succeeds " ;
53+ };
5254
53- @ Override
54- public String getName () {
55- return "greeting service" ;
55+ assertThat (invocationCount ).hasValue (0 );
56+ assertThat (retryTemplate .execute (retryable )).isEqualTo ("always succeeds" );
57+ assertThat (invocationCount ).hasValue (1 );
58+ }
59+
60+ @ Test
61+ void retryWithSuccessAfterInitialFailures () throws Exception {
62+ AtomicInteger invocationCount = new AtomicInteger ();
63+ Retryable <String > retryable = () -> {
64+ if (invocationCount .incrementAndGet () <= 2 ) {
65+ throw new Exception ("Boom!" );
5666 }
67+ return "finally succeeded" ;
5768 };
5869
59- retryTemplate . setBackOffPolicy ( new FixedBackOff ( Duration . ofMillis ( 10 )) );
60-
61- assertThat (retryTemplate . execute ( retryable )). isEqualTo ( "hello world" );
70+ assertThat ( invocationCount ). hasValue ( 0 );
71+ assertThat ( retryTemplate . execute ( retryable )). isEqualTo ( "finally succeeded" );
72+ assertThat (invocationCount ). hasValue ( 3 );
6273 }
6374
6475 @ Test
65- void retryWithFailure () {
66- Exception exception = new Exception ("Error while invoking greeting service" );
76+ void retryWithExhaustedPolicy () {
77+ AtomicInteger invocationCount = new AtomicInteger ();
78+ RuntimeException exception = new RuntimeException ("Boom!" );
6779
6880 Retryable <String > retryable = new Retryable <>() {
6981 @ Override
70- public String execute () throws Exception {
82+ public String execute () {
83+ invocationCount .incrementAndGet ();
7184 throw exception ;
7285 }
7386
7487 @ Override
7588 public String getName () {
76- return "greeting service " ;
89+ return "test " ;
7790 }
7891 };
7992
80- retryTemplate .setBackOffPolicy (new FixedBackOff (Duration .ofMillis (10 )));
81-
93+ assertThat (invocationCount ).hasValue (0 );
8294 assertThatExceptionOfType (RetryException .class )
8395 .isThrownBy (() -> retryTemplate .execute (retryable ))
84- .withMessage ("Retry policy for operation 'greeting service ' exhausted; aborting execution" )
96+ .withMessage ("Retry policy for operation 'test ' exhausted; aborting execution" )
8597 .withCause (exception );
98+ // 4 = 1 initial invocation + 3 retry attempts
99+ assertThat (invocationCount ).hasValue (4 );
86100 }
87101
88102 @ Test
89- void retrySpecificException () {
90-
91- @ SuppressWarnings ("serial" )
92- class TechnicalException extends Exception {
93- public TechnicalException (String message ) {
94- super (message );
95- }
96- }
97-
98- TechnicalException technicalException = new TechnicalException ("Error while invoking greeting service" );
103+ void retryWithFailingRetryableAndCustomRetryPolicy () {
104+ AtomicInteger invocationCount = new AtomicInteger ();
105+ RuntimeException exception = new NumberFormatException ();
99106
100107 Retryable <String > retryable = new Retryable <>() {
101108 @ Override
102- public String execute () throws TechnicalException {
103- throw technicalException ;
109+ public String execute () {
110+ invocationCount .incrementAndGet ();
111+ throw exception ;
104112 }
105113
106114 @ Override
107115 public String getName () {
108- return "greeting service" ;
109- }
110- };
111-
112- RetryPolicy retryPolicy = () -> new RetryExecution () {
113- int retryAttempts ;
114-
115- @ Override
116- public boolean shouldRetry (Throwable throwable ) {
117- return (this .retryAttempts ++ < 3 && throwable instanceof TechnicalException );
116+ return "always fails" ;
118117 }
119118 };
120119
120+ AtomicInteger retryCount = new AtomicInteger ();
121+ // Custom RetryPolicy that only retries for a NumberFormatException and max 5 retry attempts.
122+ RetryPolicy retryPolicy = () -> throwable -> (retryCount .incrementAndGet () <= 5 && throwable instanceof NumberFormatException );
121123 retryTemplate .setRetryPolicy (retryPolicy );
122- retryTemplate .setBackOffPolicy (new FixedBackOff (Duration .ofMillis (10 )));
123124
125+ assertThat (invocationCount ).hasValue (0 );
126+ assertThat (retryCount ).hasValue (0 );
124127 assertThatExceptionOfType (RetryException .class )
125128 .isThrownBy (() -> retryTemplate .execute (retryable ))
126- .withMessage ("Retry policy for operation 'greeting service' exhausted; aborting execution" )
127- .withCause (technicalException );
129+ .withMessage ("Retry policy for operation 'always fails' exhausted; aborting execution" )
130+ .withCause (exception );
131+ // 6 = 1 initial invocation + 5 retry attempts
132+ assertThat (invocationCount ).hasValue (6 );
133+ assertThat (retryCount ).hasValue (6 );
128134 }
129135
130136}
0 commit comments