toosensitive
toosensitive

Reputation: 2375

testing Spring Retry throws an error: wanted * times, but was 1 time

I use spring retry and want to unit test it. but could not get unit test working. See My code & test below.
When I run the test, it says expected 3 times but was 1 time. I must miss something. thanks

 @RunWith(SpringJUnit4ClassRunner.class)
 public class MyOperationsTest
 private MyOperations myOperations;
 @mock 
 Param1 param1;
 @mock 
 Param2 param2AnotherOperation; 

 @Before 
 public void setUp(){
     myOperations = new MyOperations(param1, param2AnotherOperation);     
 }
 @Test
    public void testmyMethodWithFailure_ShouldRetry3TimesThenThrowException() {
        MyException exception1 = new MyException ("exception 1");
        MyException exception2 = new MyException ("exception 2");
        MyException exception3 = new MyException ("exception 3");

        Mockito.doThrow(exception1).when(param2AnotherOperation).itsMethod(firstParam, secondParam);
        Mockito.doThrow(exception2).when(param2AnotherOperation).itsMethod(firstParam, secondParam);
        Mockito.doThrow(exception3).when(param2AnotherOperation).itsMethod(firstParam, secondParam);

        Throwable writingException =
                catchThrowable(() -> myOperations.myMethod(firstParam, secondParam));

        Mockito.verify(param2AnotherOperation, times(3)).itsMethod(firstParam, secondParam);
    }

in MyOperations class

@Service
@EnableRetry
    public class MyOperations{
    Param1 param1;
    Param2AnotherOperation param2AnotherOperation
    public MyOperations(Param1 param1, Param2AnotherOperation param2AnotherOperation) {
        this.param1 = param1;
        this.param2AnotherOperation = param2AnotherOperation;

}

    @Retryable(value = {MyExceptoin.class},
            maxAttemptsExpression = "3",
            backoff = @Backoff(delayExpression = "#{@retryInterval}"))
            public void myMethod(firstParam, secondParam){
            try {                  
                param2AnotherOperation.itsMethod(firstParam, secondParam);
                ...                    
            }
            catch(Exception ex){
                throw new MyExceptoin(ex);
            }
        }

    }

Upvotes: 1

Views: 6573

Answers (2)

shazwashere
shazwashere

Reputation: 83

Like pointed out, for your retries to work, you need Spring to help you with that meaning you should be picking your bean from the Spring Context; so you will have to remove the explicit instantiation of the MyOperations class. Also, your dependencies to the MyOperations class need to be inserted into the SpringContext (so they can be bound together in the context itself) and this can be done by annotation your mocked beans with @MockBean. This will replace actual beans in the SpringContext with these mock beans. With all these in place, you will be able to leverage Spring's features like @Retryable in your test.

So, something like:

@RunWith(SpringRunner.class)
 public class MyOperationsTest

 @MockBean // pushed 1st mocked dependency to Spring context
 Param1 param1;

 @MockBean // pushed 2nd mocked dependency to Spring context
 Param2 param2AnotherOperation; 

 @Autowired //Getting the hydrated bean for class under test from context
 private MyOperations myOperations;

 @Before 
 public void setUp(){
 // No manual instantiation required; picking bean from context
 //    myOperations = new MyOperations(param1, param2AnotherOperation);     
...
 }

... continue with tests ...

Upvotes: 1

Gary Russell
Gary Russell

Reputation: 174554

@EnableRetry has to be on a @Configuration class; you have it on a @Service bean.

EDIT

Here's a quick hack to get you started; I haven't tested it but it should be close...

@RunWith(SpringRunner.class)
public class So52262230ApplicationTests {

    private MyOperations myOperations;

    @Autowired
    Param1 param1;

    @Autowired
    Param2 param2AnotherOperation;

    @Autowired
    MyOperations myoperations;

    @Test
    public void testmyMethodWithFailure_ShouldRetry3TimesThenThrowException() {
        MyException exception1 = new MyException("exception 1");
        MyException exception2 = new MyException("exception 2");
        MyException exception3 = new MyException("exception 3");

        Mockito.doThrow(exception1).when(param2AnotherOperation).itsMethod(firstParam, secondParam);
        Mockito.doThrow(exception2).when(param2AnotherOperation).itsMethod(firstParam, secondParam);
        Mockito.doThrow(exception3).when(param2AnotherOperation).itsMethod(firstParam, secondParam);

        Throwable writingException = catchThrowable(() -> myOperations.myMethod(firstParam, secondParam));

        Mockito.verify(param2AnotherOperation, times(3)).itsMethod(firstParam, secondParam);
    }

    @Configuration
    @EnableRetry
    public static class Config {

        @Bean
        public Param1 param1() {
            //return a mock
        }

        @Bean
        public Param2 param2() {
            //return a mock
        }

        @Bean
        public MyOperations myOperations() {
            return new MyOperations(param1(), param2());
        }
    }

}

Upvotes: 1

Related Questions