아래와 같이 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);
'삽질' 카테고리의 다른 글
main(String[] args)의 의미 (0) | 2023.07.15 |
---|---|
Java 상수 final의 초기화 방법 (0) | 2023.07.15 |
방향 이동 이중 반복문 추상화 해보기 (0) | 2023.06.08 |
카프카 토픽 삭제기 (0) | 2023.05.24 |
[Spring Boot] nested exception is ElasticsearchException[Elasticsearch exception [type=no_such_file_exception, reason=/usr/share/elasticsearch/config/settings/stop/english.txt]] (0) | 2023.05.18 |