4

I would like to write a junit test case for spring retry, i tried like the below, but the junit is not working as expected. I am calling MaxAttemptRetryService.retry method, if it fails, it has to try for max 3 times. Here, Dao is calling a rest service, that is down, hence it should go trying for maximum 3 times. hence dao.sam method must be called 3 times.

Service Class:

@Service
@EnableRetry
public class MaxAttemptRetryService {   
    @Retryable(maxAttempts=3)
    public String retry(String username) throws Exception {
        System.out.println("retry???? am retrying...");
        int h = maxAttemptDao.sam();
        return "kkkk";
    }
}

Dao class:

@Component
public class MaxAttemptDao {
    public int sam() throws Exception{
        try{
            new RestTemplate()
            .getForObject("http://localhost:8080/greeting1/{userName}", 
                    String.class, "");
        }catch(Exception e){
            throw  e;
        }
        return 0;
    }
}

Test class:

@RunWith(SpringRunner.class)
public class HystrixServiceTest {

    @InjectMocks
    private MaxAttemptRetryService maxAttemptRetryService = new MaxAttemptRetryService();

    @Mock
    private MaxAttemptDao maxAttemptDao;

    @Test
    public void ff() throws Exception{
        when(maxAttemptDao.sam()).thenThrow(Exception.class);
        maxAttemptRetryService.retry("ll");
        verify(maxAttemptDao, times(3)).sam();
    }
}
user2000189
  • 479
  • 4
  • 6
  • 22
  • Why should an instance created by yourself work with a retry? Retry is enabled through AOP, which only works in a Spring based app in t his case. Instead use `@MockBean` on your `MaxAttemptDao`, and `@Autowire` the `MaxAttemptRetryService` and run with `@SpringBootTest`. – M. Deinum Jan 09 '20 at 13:18
  • @M.Deinum can you provide some example for this retry – user2000189 Jan 09 '20 at 13:21

2 Answers2

5

@EnableRetry and @Retryable annotations should be processed by spring that is supposed to generate a proxy on-the-fly in runtime out of the DAO. The proxy will add the functionality of retry.

Now when you're running a test, I don't see that it runs spring at all. You mentioned that you're running Spring Boot, but you don't use @SpringBootTest. On the other hand you also don't specify the configuration to load the class from (@ContextConfiguration annotation on HystrixServiceTest class)

So I conclude that you don't initialize spring correctly and it can't process the @Retry annotation correctly as a result.

Additional things that seem wrong to me:

You should use @MockBean (if you start spring properly in the test) so that it won't just create a @Mock (for which you need a mockito runner BTW) but will create a mock spring bean and register it in an application context effectively overriding a standard bean declaration.

I think you should do something like this:

@RunWith(SpringRunner.class)
@SpringBootTest
public class HystrixServiceTest {

  @Autowired // if everything worked right, you should get a proxy here actually (you can check that in debugger)
  private MaxAttemptRetryService maxAttemptRetryService;

  @MockBean
  private MaxAttemptDao maxAttemptDao;


  @Test
  public void ff() throws Exception{
      when(maxAttemptDao.sam()).thenThrow(Exception.class);
      maxAttemptRetryService.retry("ll");
      verify(maxAttemptDao, times(3)).sam();
  }


}
Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • 2
    Just one remark to this excellent answer: with this solution you'll never get to the verify-call, because the Exception will be re-thrown when the retries are exceeded. You could either wrap the call to retry in try-catch or use assertThrows from Junit Jupiter to make sure the verification gets executed. – Sebastian Heikamp Jan 09 '20 at 13:31
  • 1
    What exactly doesn't work? Please update the question with the test in its current form (after you've applied the fix) and add the stack trace. Otherwise we won't be able to help unfortunately – Mark Bramnik Jan 09 '20 at 13:35
  • @CliveBixby Yes after adding try catch block it is working – user2000189 Jan 10 '20 at 09:31
1

To add to the answer above: alternatively and for faster loading of the context, the following config can be used:

@RunWith(SpringJUnit4ClassRunner.class)
@EnableRetry
@ContextConfiguration(classes = { MaxAttemptRetryService.class })
public class HystrixServiceTest {

  @Autowired
  private MaxAttemptRetryService maxAttemptRetryService;

  @MockBean
  private MaxAttemptDao maxAttemptDao;


  @Test
  public void ff() throws Exception{
      when(maxAttemptDao.sam()).thenThrow(Exception.class);
      maxAttemptRetryService.retry("ll");
      verify(maxAttemptDao, times(3)).sam();
  }


}
catch22
  • 1,564
  • 1
  • 18
  • 41