스프링 공부/게시판 프로젝트 만들기

[스프링] 31. MemberController-회원가입 기능 구현하기, Null/Success 테스트

장아장 2023. 2. 12. 15:33

이제 컨트롤러를 좀 만들어보며, 테스트도 해보려고 한다. 

일단 제일 간단한 회원가입 로직부터 만들고, 테스트를 해봐야겠다. 

 

@RestController
@RequiredArgsConstructor
public class MemberController {

    private final MemberService memberService;

    @PostMapping("/join")
    public String register(@RequestBody @Valid RegisterRequestDto registerRequestDto){
        memberService.registerNewMember(registerRequestDto);
        return "success register";
    }
}

간단하게 만들었다. 

일단, RegisterRequestDto를 body(우리가 인터넷 창을 열었을 때 흔하게 보는 페이지 부분)에 json타입으로 입력을 주고 받는다. 

그렇기 때문에, @RequestBody를 붙여주고, 

@NotNull(message = "아이디를 입력해야 합니다.")
@NotBlank(message = "아이디를 입력해야 합니다.")
private String username;

이런 식으로 @Validation을 붙여둔 dto이고, 이걸 이용하기 위해서 @Valid도 파라미터측에 붙여두었다. 

 

딱 보았을 때 기능이 단순해진다. 

그런데, 테스트를 해보려고 하니까, 도메인로직, 서비스로직, validation으로 인한 입력을 받는 순간까지 예외가 존재한다. 

이걸 전부 고려해서 테스트코드를 짜야 한다. 

경우들을 만들어보자면, 

  • 회원가입 성공
  • 입력중 null이나 공백으로 인한 예외
  • 중복된 값으로 인한 예외
  • 이메일 형식 문제가 있어 예외
  • 비밀번호가 서로 일치하지 않아 예외

글쓰면서 바로 생각난것만 해도 이정도나 된다. 

이걸 전부 한 클래스에 만들면 너무 난잡할 것 같다고 생각이 들었다. 

결국, 

  • NullOrSuccess(null의 예외나 성공)
  • Duplicated(중복된 멤버가 존재)
  • ETC(이메일 형식이나 비밀번호가 서로 일치하지 않을 때 예외

 

이 정도를 만들어보려고 한다. 

@SpringBootTest
@AutoConfigureMockMvc
class MemberControllerTest_Register_NullOrSuccess {


    @Autowired
    private MockMvc mvc;

    @Autowired
    private MemberRepository memberRepository;

    private String makeJson(Object object){
        try {
            return new ObjectMapper().writeValueAsString(object);
        } catch (JsonProcessingException e) {
            return "";
        }
    }

    @BeforeEach
    void clearDB(){
        memberRepository.deleteAll();
    }

기본적으로 세팅을 해두었다. 

SpringBootTest로 사용해야 하고, 컨트롤러 테스트를 위해 JUnit중에 Mockito를 사용해야 하므로, 두 개의 클래스 어노테이션을 추가해주었다. 

 

이후, MockMvc와 리포지토리를 자동 연결했다. 

리포지토리는 매 테스트마다 기존의 값들이 존재하면, 테스트에 영향을 줄 수 있기에 매 순간 새로운 상황에서 단위 테스트를 하기 위해 repository.deleteAll()을 해주려고 넣었다. 

 

또한, 위에서 적었던 대로 body에서는 Json타입으로 데이터가 이동된다. 

우리가 코드상에서 Json입력을 넣어주기 위해, makeJson이라는 클래스를 넣었다. 

 

private static RegisterRequestDto makeTestRegister() {
    RegisterRequestDto registerRequestDto = RegisterRequestDto.builder()
            .username("test")
            .nickname("test")
            .email("test@test.com")
            .password("pass")
            .passwordCheck("pass")
            .build();
    return registerRequestDto;
}

성공과 null값을 테스트하기 위해서, 이렇게 테스트클래스를 만들어주었다.

null값은 필요할 때 마다 setter(매 순간 null값을 넣은 생성자를 쓰기 힘들거 같아서 지금만 사용했다. )로 null부분을 만들어 줄 것이다. 

@Test
@DisplayName("회원가입을 성공하면 success register가 반환된다.")
public void registerSuccessTest() throws Exception{
    //given
    RegisterRequestDto registerRequestDto = makeTestRegister();
    //expected
    mvc.perform(MockMvcRequestBuilders.post("/join")
            .contentType(MediaType.APPLICATION_JSON)
            .content(makeJson(registerRequestDto)))
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andExpect(MockMvcResultMatchers.content().string("success register"))
            .andDo(MockMvcResultHandlers.print());
}

@Test
@DisplayName("아이디가 null값이면 400에러를 날린다.")
public void registerFail_NullUsername() throws Exception{
    //given
    RegisterRequestDto registerRequestDto = makeTestRegister();
    registerRequestDto.setUsername(null);
    //expected
    mvc.perform(MockMvcRequestBuilders.post("/join")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(makeJson(registerRequestDto)))
            .andExpect(MockMvcResultMatchers.status().isBadRequest())
            .andExpect(MockMvcResultMatchers.jsonPath("$.result.failMessage").value("아이디를 입력해야 합니다."))
            .andDo(MockMvcResultHandlers.print());
}
두 경우의 테스트를 만들었다.
첫 번째는 성공했을 때이고, 두 번째는 아이디가 null값이어서 실패했을 때 이다. 
validation이 있기 때문에, 넣어둔 아이디를 입력해야 합니다가 나올 것 이라는 생각으로 만들었다. 

 

회원가입은 성공하는데, null값일 때 성공하지 못했다. 

이유를 보아하니, 

일단 validation의 어노테이션으로 인한 예외가 발생했을 때, 400에러까지는 발생시키지만, body에 반환하는 값이 없어서 그런 것을 확인했다. 

그렇다면, 어떻게 해주어야 할까?

ExceptionAdvice를 새로 만들었다. 

여기에서 Response.failure로 반환하는 방법을 만들어보려고 한다. 

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Response needToWriteParameterException(MethodArgumentNotValidException e){
    return Response.failure(400, e.getFieldErrors().get(0).getDefaultMessage());
}

이런식으로 간략하게 예외처리를 실행시켰다. 

기본적으로 Validation클래스로 인한 예외처리는 MethodArgumentNotValidException.class로 넘어간다. 

이걸 잡아서, 예외 내에 있는 DefaultMessage를 빼내면, 우리가 @NotNull, @NotEmpty, @NotBlank에 집어넣은 message가 나온다. 

이걸 받아와서, 400에러가 반환되게 수정했다. 

다시 실행하면, 지금과 같이 잘 나오게 된다. 

똑같은 방식으로 닉네임, 이메일, 비밀번호, 비밀번호재입력까지 전부 만들어주었다. 

기본적인 null값에 대해서 성공했다. 

조금 더 만들어보고 싶은 것은, null이 여러 개 일 때 어떻게 예외를 넘기는게 제일 깔끔할지 생각해봐야 겠다.