본문 바로가기

삽질

WebMvcTest Mockito given() Null

아래와 같이 TDD로 회원 가입 단위 테스트를 구현하고 있었습니다.

@Entity
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String password;

    public User copyOf(){
        return User.builder()
                .name(this.name)
                .password(this.password)
                .build();
    }

    public String toJson() throws JsonProcessingException {
        return new ObjectMapper().writeValueAsString(User.builder().name(name).password(password).build());
    }
}
@RestController
@RequestMapping("/api/v1/user")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/signup")
    private ResponseEntity<User> signUp(@RequestBody User user){
        User save = userService.save(user);
        return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(save);
    }
}
@WebMvcTest(UserController.class)
public class UserControllerTest {

    private static final String prefix = "/api/v1/user";

    @MockBean
    private UserService userService;

    @Autowired
    private MockMvc mockMvc;

    @Test
    @DisplayName("회원 가입")
    public void signUp() throws Exception {
        Long id = 1L;
        String name = "ksb";
        String password = "1234";

        User user = User.builder().name(name).password(password).build();

        given(userService.save(user)).willReturn(user);

        RequestBuilder request = MockMvcRequestBuilders
                .post(prefix +"/signup")
                .content(user.toJson())
                .contentType(MediaType.APPLICATION_JSON);

        mockMvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value(name))
                .andExpect(jsonPath("$.password").value(password));
    }

 

근데, 기대와 다르게 테스트는 통과되지 않았습니다.

아래와 같은 문제가 발생했습니다.

No value at JSON path "$.name"
java.lang.AssertionError: No value at JSON path "$.name"

 

받은 결과를 보니 응답 Body에 아무런 데이터가 없었습니다.

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

 

분명히 테스트에서 given으로 반환 값를 넘겨주는데도 반환 값이 없었습니다.

User user = User.builder().name(name).password(password).build();
given(userService.save(user)).willReturn(user);

 

혹시나 해서 User 객체에 Serializable을 붙여봤지만, 결과는 같았습니다.

 

그러다 발견한 것이 있습니다.

RequestBuilder request = MockMvcRequestBuilders
    .post(prefix +"/signup")
    .content(user.toJson())
    .contentType(MediaType.APPLICATION_JSON);

 

위에서 주목할 곳은 user.toJson()입니다.

위 요청은 user 객체를String으로 바꾸고, Controller에서 @RequestBody에 의해 매핑됩니다.

 

즉, Controller의 user는 새로 만들어지기 때문에, Test의 user와 다른 인스턴스입니다.

 

이를 어떻게 해결할 까 생각하다가 도저히 같은 인스턴스를 만들어서 전송할 방법이 생각나지 않다가,

한 가지 꼼수가 떠올랐습니다.

바로 any(Class<T> type)를 사용하는 것입니다.

any(Class<T> type)를 쓰면 파라미터와 같은 타입의 객체가 전달되면 의도된 값이 반환하도록 만들 수 있기 때문입니다.

given(userService.save(any(User.class))).willReturn(user);