0

I have the following controller:

RestApiController.java

package com.spring.ocr.rest.restconsume.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;

import com.spring.ocr.rest.restconsume.model.User;
import com.spring.ocr.rest.restconsume.service.UserService;
import com.spring.ocr.rest.restconsume.util.CustomErrorType;

@RestController
@RequestMapping("/api")
public class RestApiController {

    @Autowired
    UserService userService;

    @RequestMapping(method = RequestMethod.GET, value = "/user/")
    public ResponseEntity<List<User>> listAllUsers() {
        final List<User> users = userService.findAllUsers();
        if(users.isEmpty()) {
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        }
        return new ResponseEntity<List<User>>(users, HttpStatus.OK);
    }

    @RequestMapping(method = RequestMethod.GET, value = "/user/{id}")
    public ResponseEntity<?> getUser(@PathVariable("id") long id) {
        final User user = userService.findUserById(id);
        if(user == null) {
            return new ResponseEntity<>(new CustomErrorType(String.format("User with id %s not found", id)), HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity<User>(user, HttpStatus.OK);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/user/")
    public ResponseEntity<?> createUser(@RequestBody User user, UriComponentsBuilder ucBuilder) {
        if(userService.doesUserExist(user)) {
            return new ResponseEntity<>(new CustomErrorType(String.format("Unable to create, user %s already exists", user.getName())), HttpStatus.CONFLICT);
        }
        userService.createUser(user);

        final HttpHeaders headers = new HttpHeaders();
        headers.setLocation(ucBuilder.path("/api/user/{id}").buildAndExpand(user.getId()).toUri());
        return new ResponseEntity<String>(headers, HttpStatus.CREATED);
    }

    @RequestMapping(method = RequestMethod.PUT, value = "/user/{id}")
    public ResponseEntity<?> updateUser(@PathVariable("id") long id,@RequestBody User user) {
        User currentUser = userService.findUserById(id);
        if(currentUser == null) {
            return new ResponseEntity<>(new CustomErrorType(String.format("Unable to create update, User with id %s not found", user.getId())), HttpStatus.NOT_FOUND);
        }
        currentUser.setName(user.getName());
        currentUser.setAge(user.getAge());
        currentUser.setSalary(user.getSalary());

        userService.updateUser(currentUser);
        return new ResponseEntity<User>(currentUser, HttpStatus.OK);
    }

    @RequestMapping(method = RequestMethod.DELETE, value = "/user/{id}")
    public ResponseEntity<?> deleteUser(@PathVariable("id") long id) {
        User user = userService.findUserById(id);

        if(user == null) {
            return new ResponseEntity<>(new CustomErrorType(String.format("Unable to delete user, user with id %s not found", id)), HttpStatus.NOT_FOUND);
        }
        userService.deleteUserById(id);
        return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
    }

    @RequestMapping(method = RequestMethod.DELETE, value = "/user/")
    public ResponseEntity<User> deleteAllUsers() {
        userService.deleteAllUsers();
        return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
    }
 }

And I've set up a test of the web layer using mockMvc, with the user service bean mocked out as is standard:

RestApiControllerUnitTest.java

package com.spring.ocr.rest.restconsume.controller;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.util.ArrayList;
import java.util.List;

import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.spring.ocr.rest.restconsume.model.User;
import com.spring.ocr.rest.restconsume.service.UserService;

@RunWith(SpringRunner.class)
@WebMvcTest(RestApiController.class)
public class RestApiControllerUnitTest {

    @Autowired
    private MockMvc mockMvc;
    @MockBean
    UserService userService;
    @Autowired
    ObjectMapper objectMapper;

    private final List<User> dummyUserList = getDummyUserList();
    private final User dummyUser = new User((long)1, "Dave", (short)30, (double)30000);
    private final User dummyUpdatedUser = new User((long)1, "David", (short)31, (double)35000);

    @Test
    public void test_listAllUsers_userListSizeIs4_returnsListSizeOf4AndOkStatus() throws Exception {
        when(userService.findAllUsers()).thenReturn(dummyUserList);
        this.mockMvc.perform(get("/api/user/"))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$", hasSize(4)));
    }

    @Test
    public void test_listAllUsers_userListIsEmpty_returnsNoContentStatus() throws Exception {
        when(userService.findAllUsers()).thenReturn(new ArrayList<User>());
        this.mockMvc.perform(get("/api/user/"))
            .andDo(print())
            .andExpect(status().isNoContent());
    }

    @Test
    public void test_getUser_userExists_returnsUser() throws Exception {
        when(userService.findUserById(dummyUser.getId())).thenReturn(dummyUser);
        this.mockMvc.perform(get("/api/user/" + dummyUser.getId()))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.id",is((int)dummyUser.getId())))
            .andExpect(jsonPath("$.name", is(dummyUser.getName())))
            .andExpect(jsonPath("$.age", is((int)dummyUser.getAge())))
            .andExpect(jsonPath("$.salary", is(dummyUser.getSalary())));
    }

    @Test
    public void test_getUser_userDoesntExist_returnsNotFoundStatusAndCustomErrorString() throws Exception {
        when(userService.findUserById(dummyUser.getId())).thenReturn(null);
        this.mockMvc.perform(get("/api/user/"+dummyUser.getId()))
            .andDo(print())
            .andExpect(status().isNotFound())
            .andExpect(content().string(containsString("User with id 1 not found")));
    }

    @Test
    public void test_createUser_userDoesNotExist_userCreated() throws Exception {
        final String dummyUserJson = objectMapper.writeValueAsString(dummyUser);
        when(userService.doesUserExist(dummyUser)).thenReturn(false);
        MvcResult result = this.mockMvc.perform(post("/api/user/")
            .contentType(MediaType.APPLICATION_JSON)
            .content(dummyUserJson))
            .andDo(print())
            .andExpect(status().isCreated())
            .andReturn();
        final String header = result.getResponse().getHeader("Location");
        assertThat(header, is("http://localhost/api/user/1"));
    }

    @Test
    public void test_createUser_userExists_returnsNotFoundStatusAndCustomErrorMessage() throws Exception {
        final String dummyUserJson = objectMapper.writeValueAsString(dummyUser);
        final String expectedContent = String.format("Unable to create, user %s already exists", dummyUser.getName());
        when(userService.doesUserExist(anyObject())).thenReturn(true);
        this.mockMvc.perform(post("/api/user/")
            .contentType(MediaType.APPLICATION_JSON)
            .content(dummyUserJson))
            .andDo(print())
            .andExpect(status().isConflict())
            .andExpect(jsonPath("$.errorMessage", is(expectedContent)));
    }

    @Test
    public void test_updateUser_userExists_returnsOkStatusAndUpdatedUser() throws Exception {
        final String dummyUpdatedUserJson = objectMapper.writeValueAsString(dummyUpdatedUser);
        when(userService.findUserById(dummyUser.getId())).thenReturn(dummyUser);
        doNothing().when(userService).updateUser(dummyUser);
        this.mockMvc.perform(put("api/user/" + dummyUser.getId())
            .contentType(MediaType.APPLICATION_JSON)
            .content(dummyUpdatedUserJson))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.age", is(dummyUpdatedUser.getAge())))
            .andExpect(jsonPath("$.name", is(dummyUpdatedUser.getName())))
            .andExpect(jsonPath("$.salary", is(dummyUpdatedUser.getSalary())));
    }

    @Test
    public void test_deleteUser_userExists_returnNoContentStatus() throws Exception {
        when(userService.findUserById(dummyUser.getId())).thenReturn(dummyUser);
        this.mockMvc.perform(delete("api/user/" + dummyUser.getId()))
            .andDo(print())
            .andExpect(status().isNotFound());
    }

    @Test
    public void test_deleteUser_userDoesntExist_returnsNotFoundStatusAndCustomErrorMessage () throws Exception {
        when(userService.findUserById(dummyUser.getId())).thenReturn(null);
        final String expectedContent = String.format("Unable to create update, User with id %s not found", dummyUser.getName());
        this.mockMvc.perform(delete("api/user/"+dummyUser.getId()))
            .andDo(print())
            .andExpect(status().isNotFound())
            .andExpect(jsonPath("$.errorMessage", is(expectedContent)));
    }

    @Test
    public void test_deleteAllUsers_usersListPopulated_returnNoContentStatus() throws Exception {
        this.mockMvc.perform(delete("api/user/").contentType(MediaType.APPLICATION_JSON))
            .andDo(print())
            .andExpect(status().isNotFound());
    }

    private List<User> getDummyUserList() {
        final List<User> dummyUserList = new ArrayList<>();
        dummyUserList.add(new User((long)1, "Dave", (short)30, (double)30000));
        dummyUserList.add(new User((long)2, "Jess", (short)20, (double)20000));
        dummyUserList.add(new User((long)3, "Mike", (short)40, (double)40000));
        dummyUserList.add(new User((long)4, "Molly", (short)50, (double)50000));
        return dummyUserList;
    }
}

The test test_updateUser_userExists_returnsOkStatusAndUpdatedUser is returning a 404 rather than a 200 status and test_deleteUser_userDoesntExist_returnsNotFoundStatusAndCustomErrorMessage is not returning the error message in the body, which alludes to the 404 not being a "genuine" 404(its not returned because the correct response is coming back, its returning it for some other reason). Im also thinking that some of the other 404 statuses may be returned under the same context.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
jbailie1991
  • 1,305
  • 2
  • 21
  • 42

1 Answers1

1

Issue was due to missing "/" on the controller methods used in the failing tests.

jbailie1991
  • 1,305
  • 2
  • 21
  • 42