I have a test class that is annotated with @Spy
and @InjectMocks
and tested using Mockito. The class under test has a value (url) that is retrieved from the application.properties
file. I’d like to test whether this url is being set correctly within the method that uses it. I can do this if I remove the @Spy
and @InjectMocks
annotations and use @Autowire
and @SpringBootTest
. However, that breaks other tests that use the spy functionality, so I’m just wondering if there’s any way we can keep the spy working and test all our methods inside the same file, maybe without the need to bring in the @SpringBootTest
annotation and the autowiring? The workaround we’re using for now is to have two files, one that uses the spy and the other that tests the specific method to do with the properties file and that requires the full context to load, but not sure that’s the best solution here?
Here is the test with the spy:
@ExtendWith(MockitoExtension.class) class ProviderHelperServiceTest { @Spy @InjectMocks ProviderHelperService providerHelperService; @Value("${viaduct-url}") String viaductUrl; @Test void testGetRequestBodyUriSpec() { WebClient.RequestBodyUriSpec requestBodyUriSpec = providerHelperService.getRequestBodyUriSpec("sifToken"); final String[] url = new String[1]; requestBodyUriSpec.attributes(httpHeaders -> { url[0] = (String) httpHeaders.get("org.springframework.web.reactive.function.client.WebClient.uriTemplate"); }); // Fails as url[0] comes back as null. Disabled and moved to another file. assertEquals(viaductUrl, url[0]); }
@SpringBootTest class ProviderHelperService2Test { @Autowired ProviderHelperService providerHelperService; @Value("${viaduct-url}") String viaductUrl; @Test void testGetRequestBodyUriSpec() { WebClient.RequestBodyUriSpec requestBodyUriSpec = providerHelperService.getRequestBodyUriSpec("sifToken"); final String[] url = new String[1]; requestBodyUriSpec.attributes(httpHeaders -> { url[0] = (String) httpHeaders.get("org.springframework.web.reactive.function.client.WebClient.uriTemplate"); }); assertEquals(viaductUrl, url[0]); } }
And here is the method under test:
public class ProviderHelperService { @Value("${viaduct-url}") String viaductUrl; public WebClient.RequestBodyUriSpec getRequestBodyUriSpec(String sifToken) { WebClient.RequestBodyUriSpec requestBodyUriSpec = WebClient.create().post(); requestBodyUriSpec.header("Authorization", sifToken); requestBodyUriSpec.uri(viaductUrl); return requestBodyUriSpec; } }
Advertisement
Answer
The cleanest way to perform such tests is to replace field injection with constructor injection, and then you can quite easily confirm that the value that’s passed into the class comes back out the service call.
If you’re using Boot, it’s usually best to replace use of @Value
with @ConfigurationProperties
. Depending on the specifics, you can either pass the whole properties object to the service’s constructor or write an @Bean
configuration method that unpacks the relevant properties and passes them as plain constructor parameters with new
.