This is a short series of how to use Spring in integration testing and unit testing. | ||
---|---|---|
Configurations | Mocking | Testing a REST API |
A custom configuration | Configuration logic |
After we understand how to use mocks in Spring in integration tests, let’s take a look at a setup for testing a REST service that uses a dependency we want to mock. API testing is a usual integration test scenario, and with those, we might need to mock dependencies buried under the API layer. Spring to the rescue.
Our StudentService contains an endpoint like the one below, and we’d like to mock the student in integration tests for both cases (either null or not):
@Autowired private Student student; @RequestMapping(value="/name",method = RequestMethod.GET) public String getStudentName() { String name = student.getName(); if (name == null) return "unknown"; return name; }
We’ll use the updated configuration from last time, without any behavior setup:
@Configuration public class MockInjectionConfiguration { @Bean public Student student() { Student mockStudent = Mockito.mock(Student.class); return mockStudent; } }
Mockito’s default is to return null on methods we didn’t use when on.That means that when getName() is called, null is returned.
Now we need to setup the integration test class:
@RunWith(SpringRunner.class) @SpringBootTest(classes = StudentApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ContextConfiguration(classes = { MockInjectionConfiguration.class }) public class restServiceTestWithMockedStudent { @Autowired private Student mockStudent; @LocalServerPort private int port; TestRestTemplate restTemplate; HttpHeaders headers; HttpEntity<String> entity; ResponseEntity<String> response; @Before public void setup() { headers = new HttpHeaders(); restTemplate = new TestRestTemplate(); entity = new HttpEntity<String>(null, headers); } @Test public void whenNoStudentName_returnUnknown() throws JSONException { response = restTemplate.exchange( createURLWithPort("/rest/student/name"), HttpMethod.GET, entity, String.class); assertEquals("unknown", response.getBody()); } @Test public void whenStudentExists_returnName() throws JSONException { when(mockStudent.getName()).thenReturn("Gil"); response = restTemplate.exchange( createURLWithPort("/rest/student/name"), HttpMethod.GET, entity, String.class); assertEquals("Gil", response.getBody()); } private String createURLWithPort(String uri) { return "http://localhost:" + port + uri; } }
Integration tests are a bit more elaborate than regular unit tests. Let’s break it down. Let’s take a look at the annotation part first:
@RunWith(SpringRunner.class) @ContextConfiguration(classes = { MockInjectionConfiguration.class }) @SpringBootTest(classes = StudentApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
The @RunWith and @ContextConfiguration annotations are used just like we used them before – selecting Spring as the JUnit runner, and choosing the right configuration. We’ve also let Spring Boot know which service class to run (that includes our service), and allow it to select a random port, using @SpringBootTest annotation . This port number will be injected using the @LocalServerPort annotation in the class.
In addition, we @Autowired the mockStudent. We need it for the second test (it’s not needed in the first integration test, because we’re using the default setting, returning null).
The @Before method just sets up things for the call:
@Before public void setup() { headers = new HttpHeaders(); restTemplate = new TestRestTemplate(); entity = new HttpEntity<String>(null, headers); }
After that, it’s using the TestRestTemplate to invoke the service. The first integration test checks the return value for a non-existent student:
@Test public void whenNoStudentName_returnUnknown() throws JSONException { response = restTemplate.exchange( createURLWithPort("/rest/student/name"), HttpMethod.GET, entity, String.class); assertEquals("unknown", response.getBody()); }
Since the mock is just initialized, it will return null as the name, which will cause to return the “unknown” value, the case we want to cover in the first our integration test.
In the second integration test we’re using, we’re also setting behavior on the mock for the getName() method:
@Test public void whenStudentExists_returnName() throws JSONException { when(mockStudent.getName()).thenReturn("Gil"); response = restTemplate.exchange( createURLWithPort("/rest/student/name"), HttpMethod.GET, entity, String.class); assertEquals("Gil", response.getBody()); }
That’s it so far. We may continue on integration tests more later.
0 Comments