거창한 회고는 아니다.
그냥 오랜만에 혼자 천천히 진행하고 있는, 학원의 종합 프로젝트를 진행하면서 무기한 보류 중인,
개인프로젝트의 코드를 다시 꺼내봤다.
현재 계정부분의 로그인, 회원가입만 구현되어 있는 작고 소중한 프로젝트이며,
여기서 구현 완료된 계정 부분의 컨트롤러 테스트 코드를 작성해 보았다.
테스트 코드 작성 전 나의 JOIN API
public class MemberRestController {
@PostMapping("/join")
public Response<MemberJoinResponse> join(@RequestBody @Valid final MemberJoinRequest dto) {
MemberDto member = memberService.join(dto);
return Response.success(
MemberJoinResponse.builder()
.memberEmail(member.getMemberEmail())
.memberNickname(member.getMemberNickname())
.memberStatus(member.getMemberStatus())
.build()
);
}
해당 코드를 가지고 테스트 코드를 작성하려고 보니..
어..? 복잡하다.
라는 생각을 먼저 가지게 되었다.
- memberService의 join을 사용했는데 왜 DTO로 받아서 그걸 또 MemberJoinResponse로 변환해서 다시 리스폰할까..
- controller에서 builder를 써서 보내는 게 코드가 너무 길어져 보기가 안 좋다.
테스트 코드를 작성하면 결국
- MemberJoinRequest로 join을 given 한다.
- MemberJoinResponse를 return 받는다.
두 가지 로직을 컨트롤러에서 행하면 되는 것이었다.
테스트 코드 작성
@WebMvcTest(MemberRestController.class)
@WithMockUser
class MemberRestControllerTest {
...
@Test
@DisplayName("회원가입 성공")
void success_join_member() throws Exception {
MemberJoinRequest request = new MemberJoinRequest("a@naver.com", "1234", "nickname", "1q2w3e4r", MemberStatus.ADMIN);
MemberJoinResponse response = new MemberJoinResponse(1l, "a@naver.com", "nickname", 1l);
given(memberService.join(any())).willReturn(response);
String url = "/api/v1/members/join";
mockMvc.perform(post(url).with(csrf())
.header(HttpHeaders.AUTHORIZATION, "Bearer ")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(request)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultCode").exists())
.andExpect(jsonPath("$.resultCode").value("SUCCESS"))
.andExpect(jsonPath("$.result.memberNo").exists())
.andExpect(jsonPath("$.result.memberNo").value(1))
.andExpect(jsonPath("$.result.memberEmail").exists())
.andExpect(jsonPath("$.result.memberEmail").value("a@naver.com"))
.andExpect(jsonPath("$.result.memberNickname").exists())
.andExpect(jsonPath("$.result.memberNickname").value("nickname"))
.andExpect(jsonPath("$.result.blogNo").exists())
.andExpect(jsonPath("$.result.blogNo").value(1))
.andDo(print());
}
}
이러한 테스트 코드를 작성하고 다시 컨트롤러에 있는 코드를 수정했다.
테스트 코드 작성 후 수정된 컨트롤러 코드
...
public class MemberRestController {
private final MemberService memberService;
@PostMapping("/join")
public Response<MemberJoinResponse> join(@RequestBody @Valid final MemberJoinRequest request) {
MemberJoinResponse member = memberService.join(request);
return Response.success(member);
}
}
뭐 더 코드를 줄이고 싶다면
...
public class MemberRestController {
...
@PostMapping("/join")
public Response<MemberJoinResponse> join(@RequestBody @Valid final MemberJoinRequest request) {
return Response.success(memberService.join(request));
}
}
이렇게 합칠 수도 있을 것 같다.
결론
테스트 코드를 짜는 데에는 많은 이유가 있을 것이다.
대표적으로 미리 발생할 수 있는 오류들을 검증하고 대비하는 이유도 있다.
하지만 우연찮게 열어본 프로젝트의 테스트코드를 작성하게 되면서
테스트 코드 작성이 나의 코드 가독성을 개선시킬 수 있구나,
코드를 보는 사람들을 조금 더 설득시킬 수 있는 코드를 만들 수 있다는 점을 깨닫게 되었다.
예전에 우테코 프리코스를 하면서 막연히 내 코드는 참 테스트 코드 짜기 어려운 코드구나 라는 생각을 한 적이 있었다.
어떻게 코드를 짜야하는지도 몰랐고, 수정할 줄도 모르는 수준이었다면 지금은 테스트 코드를 작성하기 위해 내 코드를 수정할 수 있는 수준까지는 성장한 것 같다.
이제 여기서 더 성장해서 왜 이 코드가 테스트 코드를 작성하기 어려운지 명확한 이유를 제시하고 다른 사람들을 설득하고, 방안을 제시할 수 수준까지 오르고 싶다.
'Server > Spring&Spring Boot' 카테고리의 다른 글
[Spring] TomcatServletWebServerFactory Port 설정 (0) | 2023.01.28 |
---|---|
[Spring Boot] AWS S3에 파일 업로드 (0) | 2023.01.26 |
[Spring Boot] SpringBoot 3.0.x 이상에서 Swagger 사용 (0) | 2023.01.19 |
[Spring Boot] Spring Security 6.0 Configuration. (0) | 2023.01.17 |
[JPA] Update 후 해당 객체를 Return했을 때 @CreatedDate가 null 이 반환되는 상태에 대한 회고 (0) | 2023.01.06 |