UserController에는 회원가입, 로그인 기능을 제공하는 controller 메서드가 존재한다.
아래 예시에서 회원가입 컨트롤러 메서드의 응답은 userService.join(...) 메서드 수행 결과에 의존한다.
userService.join(...) 메서드에서 로직 수행 결과 받을 수 있는 개발자가 정의한 응답 가지의 수는 2가지이다.
- request한 username이 이미 가입되어 있는 상황일 때 exception이 발생하고,
- 아니면 정상 수행 응답하는 경우로 총 2가지가 있다.
테스트에서 HTTP Request 할 때, mockMvc.perform(post("/api/v1/users/join"). ... 같은 메서드로 호출하면 테스트 할 수 있는 것 확인할 수 있었음.
회원가입 테스트는 이 controller 수행 후
- 회원가입이 정상 수행 될 때와
- exception이 발생하는 경우
에 서버에서 정의한 HttpStatus Error 코드를 응답하는지 확인하는것이 주요 목적이므로, userService.join(...) 메서드 수행 구현은 중요하지 않고, mock 객체 만들어서 정상 수행된 경우와 exception 발생한 경우 특정 응답을 return하도록 정의만 하면 된다.
로그인 기능 컨트롤러 메서드 구현된 것을 뜯어보면 composition 방식으로 2개 인스턴스 사용하고 있는 것 확인 가능하다.
- userEntityRepository.findByUserName(...)
- 회원가입이 안 된 userName인지 체크
- encoder.matches(...)
- 비밀번호가 맞는지 체크
그리고 외부 인스턴스 메서드 사용하는 부분 결과에 따라 exception 처리를 하고 있으므로, 컨트롤러에서 응답하는 HTTP Status 경우의 수로 테스트를 작성한다면, 로그인 컨트롤러 수행 결과에서는 발생 가능한 경우의 수가 3가지 있다.
테스트는 아래 쭉 내리면 code 부분에 작성해 놓았다.
when(userService.join(userName, password)).thenThrow(new SnsApplicationException(ErrorCode.DUPLICATED_USER_NAME,""));
위 코드 실행 후 정의한 SnsApplicationException 인스턴스 ErrorCode Enum 변수에 ErrorCode.DUPLICATED_USER_NAME 저장하고, ErrorCode 인스턴스 Enum에 HttpStatus, String자료형 값을 가지는데, Mock 라이브러리 원리는 모르겠지만, 사용자 정의 Exception 클래스 ErrorCode enum 변수에 HttpStatus status 명칭으로 저장한 enum 값을 mockResponse.status에 넣어줘서 테스트 통과하는 것으로 보임 (정확한 수행 로직은 추후 확인할 것)
아래는 테스트 코드 작성 할 UserController 클래스
package com.example.sns.controller;
import com.example.sns.controller.request.UserJoinRequest;
import com.example.sns.controller.request.UserLoginRequest;
import com.example.sns.controller.response.Response;
import com.example.sns.controller.response.UserJoinResponse;
import com.example.sns.controller.response.UserLoginResponse;
import com.example.sns.model.User;
import com.example.sns.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/users")
public class UserController {
private final UserService userService;
// TDOO : implement
@PostMapping("/join")
public Response<UserJoinResponse> join(@RequestBody UserJoinRequest request){
User user = userService.join(request.getUserName(), request.getPassword());
return Response.success(UserJoinResponse.fromUser(user));
}
@PostMapping("/login")
public Response<UserLoginResponse> login(@RequestBody UserLoginRequest request){
String token = userService.login(request.getUserName(), request.getPassword());
return Response.success(new UserLoginResponse(token));
}
}
아래는 회원 가입 기능에 대한 UserControllerTest 코드 예시
package com.example.sns.controller;
import com.example.sns.controller.request.UserJoinRequest;
import com.example.sns.controller.request.UserLoginRequest;
import com.example.sns.exception.ErrorCode;
import com.example.sns.exception.SnsApplicationException;
import com.example.sns.model.User;
import com.example.sns.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private UserService userService;
@Test
public void 회원가입() throws Exception{
String userName = "userName";
String password = "password";
// TODO : mocking
when(userService.join(userName, password)).thenReturn(mock(User.class));
mockMvc.perform(post("/api/v1/users/join")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(new UserJoinRequest(userName, password)))
).andDo(print())
.andExpect(status().isOk());
}
@Test
public void 회원가입시_이미_회원가입된_userName으로_회원가입을_하는경우_에러반환() throws Exception{
String userName = "userName";
String password = "password";
when(userService.join(userName, password)).thenThrow(new SnsApplicationException(ErrorCode.DUPLICATED_USER_NAME,""));
mockMvc.perform(post("/api/v1/users/join")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(new UserJoinRequest(userName, password)))
).andDo(print())
.andExpect(status().isConflict());
}
@Test
public void 로그인() throws Exception{
String userName = "userName";
String password = "password";
// TODO : mocking
when(userService.login(userName, password)).thenReturn("test_token");
mockMvc.perform(post("/api/v1/users/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(new UserLoginRequest(userName, password)))
).andDo(print())
.andExpect(status().isOk());
}
@Test
public void 로그인시_회원가입이_안된_userName을_입력할경우_에러반환() throws Exception{
String userName = "userName";
String password = "password";
// TODO : mocking
when(userService.login(userName, password)).thenThrow(new SnsApplicationException(ErrorCode.USER_NOT_FOUND));
mockMvc.perform(post("/api/v1/users/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(new UserLoginRequest(userName, password)))
).andDo(print())
.andExpect(status().isNotFound());
}
@Test
public void 로그인시_틀린_password를_입력할경우_에러반환() throws Exception{
String userName = "userName";
String password = "password";
// TODO : mocking
when(userService.login(userName, password)).thenThrow(new SnsApplicationException(ErrorCode.INVALID_PASSWORD));
mockMvc.perform(post("/api/v1/users/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(new UserLoginRequest(userName, password)))
).andDo(print())
.andExpect(status().isUnauthorized());
}
}
'테스트 > TDD' 카테고리의 다른 글
테스트 코드 작성 순서 (0) | 2023.04.24 |
---|---|
TDD (0) | 2023.04.09 |
댓글