I am trying to write unit tests for a class having spring retry using the springRunner. But my @Autowired beans are null . Could you please let me know what I did wrong?
Below is my test class
@RunWith(SpringRunner.class)
@ContextConfiguration
public class DeltaHelperTest {
@Autowired
private DeltaHelper deltaHelper;
@Before
public void setUp() { System.setProperty("delta.process.retries", "2"); }
@After
public void validate() { validateMockitoUsage(); }
@Test
public void retriesAfterOneFailAndThenPass() throws Exception {
when(deltaHelper.restService.call(any(), any())).thenThrow(new HttpException());
deltaHelper.process(any(),any());
verify(deltaHelper, times(2)).process(any(), any());
}
@Configuration
@EnableRetry
@EnableAspectJAutoProxy(proxyTargetClass=true)
@Import(MockitoSkipAutowireConfiguration.class)
public static class Application {
@Bean
public DeltaHelper deltaHelper() {
DeltaHelper deltaHelper = new DeltaHelper();
deltaHelper.myStorageService= myStorageService();
deltaHelper.restService = restService();
return deltaHelper;
}
@Bean
public MyStorageService myStorageService() {
return new MyStorageService();
}
@Bean
public MyRestService restService() {
return new MyRestService();
}
@Bean
public MyRepo myRepository() {
return mock(MyRepo.class);
}
}
@Configuration
public static class MockitoSkipAutowireConfiguration {
@Bean MockBeanFactory mockBeanFactory() {
return new MockBeanFactory();
}
private static class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockingDetails(bean).isMock();
}
}
}
}
Here test service is null on deltaHelper object .
MyRepo.class is mocked as it has some more @autowired bean reference
Attaching other classes here
@Component
public class DeltaHelper {
@Autowired
MyRestService restService;
@Autowired
MyStorageService myStorageService;
@NotNull
@Retryable(
value = Exception.class,
maxAttemptsExpression = "${delta.process.retries}"
)
public String process(String api, HttpEntity<?> entity) {
return restService.call(api, entity);
}
@Recover
public String recover(Exception e, String api, HttpEntity<?> entity) {
myStorageService.save(api);
return "recover";
}
}
@Service
public class MyStorageService {
@Autowired
MyRepo myRepo;
@Async
public MyEntity save(String api) {
return myRepo.save(new MyEntity(api, System.currentTimeMillis()));
}
}
public class MyRestService extends org.springframework.web.client.RestTemplate {
}
Thank you
Tried MockitoJUnitRunner, But found that @Retryable works only when running with Spring
I'm not sure why you are trying to test framework functionality such as retry. Generally, you can assume that framework components have been tested thoroughly by the framework authors.
Ignoring that, I can see at least two problems:
deltaHelper
is not a mock, but your SUT, yet you try to set up method calls. If you mock your SUT, you are no longer testing your class, you are testing the mock. If you want your call to fail, don't mock the call, but mock its dependencies (e.g.MyRestService restService
) and have calls on the dependency throw an exception.You pass
ArgumentMatchers.any()
in your real method call (the "act" part), butany()
unconditionally returnsnull
(not some magic object). If you want to act on your SUT, you must pass real values.any
is for setting up mocks or verifying calls on mocks.For completeness' sake, here's the source of
any()
: