Craig Craig - 9 months ago 55
Java Question

Unit testing spring rest controller error handling

I have a spring rest controller as detailed below

@RestController
@RequestMapping(value = "/data")
public class DataController {

private final IDataService service;
private static final Logger LOG = Logger.getLogger(DataController.class);

/**
* The Constructor.
*
* @param service
* the service
*/
@Autowired
public DataController(final IDataService service) {
this.service = service;
}

// Country
/**
* Find all countries.
*
* @return the response entity< list< country>>
*/
@RequestMapping(value = "/country", produces = Util.APPLICATION_JSON_UTF8_STRING, method = RequestMethod.GET)
public ResponseEntity<List<Country>> findAllCountries() {
List<Country> countries = new ArrayList<>();
try {
countries = this.service.findAllCountries();
return new ResponseEntity<>(countries, null, HttpStatus.OK);
} catch (final Exception e) {
LOG.error(e.getMessage(), e);
return new ResponseEntity<>(countries, null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}


I have a unit test using Mockito and Mock Mvc which tests the successful path

public class DataControllerTest implements IAbstractController {

private MockMvc mockMvc;

@Mock
private IDataService serviceMock;

@InjectMocks
private DataController dataController;

@Autowired
WebApplicationContext wac;

/**
* Sets the up.
*
* @throws Exception
* the exception
*/
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(this.dataController).build();
}

@Test
public void testFindAllCountries() throws Exception {

final Country first = new CountryBuilder().id(3L).name("USA").regionId(1L).active(true).build();
final Country second = new CountryBuilder().id(66L).name("India").regionId(2L).active(true).build();
final Country third = new CountryBuilder().id(1L).name("United Kingdom").regionId(4L).active(true).build();

when(this.serviceMock.findAllCountries()).thenReturn(Arrays.asList(first, second, third));

final ResultActions ra = this.mockMvc.perform(get("/data/country.do")).andExpect(status().isOk())
.andExpect(content().contentType(Util.APPLICATION_JSON_UTF8)).andExpect(jsonPath("$", hasSize(3)))
.andExpect(jsonPath("$[0].id", is(3))).andExpect(jsonPath("$[0].name", is("USA")))
.andExpect(jsonPath("$[0].regionId", is(1))).andExpect(jsonPath("$[0].active", is(true)))
.andExpect(jsonPath("$[1].id", is(66))).andExpect(jsonPath("$[1].name", is("India")))
.andExpect(jsonPath("$[1].regionId", is(2))).andExpect(jsonPath("$[1].active", is(true)))
.andExpect(jsonPath("$[2].id", is(1))).andExpect(jsonPath("$[2].name", is("United Kingdom")))
.andExpect(jsonPath("$[2].regionId", is(4))).andExpect(jsonPath("$[2].active", is(true)));

Assert.assertNotNull(ra);
verify(this.serviceMock, times(1)).findAllCountries();
verifyNoMoreInteractions(this.serviceMock);

}


However, I am struggling to test the catch block where an error response is returned. What are the best practices around this? Can anyone advise?

FYI the Abstract controller just contains annotations

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public interface IAbstractController {

}


So the test I am trying is

@Test(expected = Exception.class)
public void testFindAllCountriesThrowsException() throws Exception {
Mockito.doThrow(new RuntimeException()).when(this.serviceMock.findAllCountries());
this.mockMvc.perform(get("/data/country.do"));
}


Whilst this test passes, accorrding to the coverage infor from EclEmma, my catch block is still not being covered by the test

Answer Source

In your test, try using

Mockito.doThrow(new RuntimeException()).when(this.serviceMock).findAllCountries());

If you encounter a compile error, you might also use

when(this.serviceMock.findAllCountries()).thenThrow(new RuntimeException());