After playing a bit with various scenarios, here is the one way how can one achieve what was asked with minimal interventions to the main code
-
Refactor your controller to use a parameter for thirdparty server address:
@RestController public class HelloController { @Value("${api_host}") private String apiHost; @RequestMapping("/hello_to_facebook") public String hello_to_facebook() { // Ask facebook about something HttpGet httpget = new HttpGet(buildURI("http", this.apiHost, "/oauth/access_token")); String response = httpClient.execute(httpget).getEntity().toString(); // .. Do something with a response return response + "_PROCESSED"; } }
‘api_host’ equals to ‘graph.facebook.com’ in application.properties in the src/main/resources
-
Create a new controller in the src/test/java folder that mocks the thirdparty server.
-
Override ‘api_host’ for testing to ‘localhost’.
Here is the code for steps 2 and 3 in one file for brevity:
@RestController
class FacebookMockController {
@RequestMapping("/oauth/access_token")
public String oauthToken() {
return "TEST_TOKEN";
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest({"api_host=localhost",})
public class TestHelloControllerIT {
@Test
public void getHelloToFacebook() throws Exception {
String url = new URL("http://localhost:8080/hello_to_facebook").toString();
RestTemplate template = new TestRestTemplate();
ResponseEntity<String> response = template.getForEntity(url, String.class);
assertThat(response.getBody(), equalTo("TEST_TOKEN_PROCESSED"));
// Assert that facebook mock got called:
// for example add flag to mock, get the mock bean, check the flag
}
}
Is there a nicer way to do this? All feedback is appreciated!
P.S. Here are some complications I encountered putting this answer into more realistic app:
-
Eclipse mixes test and main configuration into classpath so you might screw up your main configuration by test classes and parameters: https://issuetracker.springsource.com/browse/STS-3882 Use gradle bootRun to avoid it
-
You have to open access to your mocked links in the security config if you have spring security set up. To append to a security config instead of messing with a main configuration config:
@Configuration @Order(1) class TestWebSecurityConfig extends WebSecurityConfig { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/oauth/access_token").permitAll(); super.configure(http); } }
-
It is not straightforward to hit https links in integration tests. I end up using TestRestTemplate with custom request factory and configured SSLConnectionSocketFactory.