SpringBoot 유닛테스트 - 2

박찬규·2023년 7월 4일

GoodJobProject

목록 보기
2/9

이제 유닛테스트를 이용하여 회원가입 성공 기능을 검증해보자!
Mockito는 테스트 더블(실제 구현을 대체하는 객체,Mock, Stub, Spy 등)을 생성하고 동작을 검증하는 데 사용되는 목적 프레임워크이다.
@ExtendWith(MockitoExtension.class)를 테스트 클래스에 적용하면 Mockito의 기능을 사용할 수 있다.
@InjectMocks는 테스트 대상 객체를 주입받기 위해 사용했다. 여기서는 MemberController가 해당 대상이다. Mockito는 MemberController의 인스턴스를 생성하고, 필요한 의존성 해결을 위해
@Mock 어노테이션이 지정된 객체에 의존성을 주입한다.
이렇게 생성된 Mock 객체들은 테스트에서 사용될 때 원하는 동작을 강제로 수행할 수 있다.

@ExtendWith(MockitoExtension.class)
class MemberControllerTest {

    @InjectMocks
    private MemberController memberController;

    @Mock
    private MemberService memberService;

    @Mock
    private Rq rq;

    private MockMvc mockMvc;

먼저 테스트 메서드 작성 전
@BeforeEach를 통해 테스트 메서드가 실행되기전에 초기화 작업을 정의한다. 여기서는 MockMvc 객체를 초기화해준다.

	@BeforeEach
    public void init() {
        mockMvc = MockMvcBuilders.standaloneSetup(memberController).build();
    }

standaloneSetup() 메서드를 통해 특정 컨트롤러를 기반으로 독립적인 환경에서 테스트를 실행할 수 있다.

이제, 회원가입 성공 테스트 메서드를 작성해보자! 대략적인 순서를 살펴보면,
1. 테스트에 사용할 테스트값을 넣은 객체를 생성한다.
2. doReturn().when()을 통해 Mock 객체의 동작을 설정한다.
3. ResultAction을 사용해 post(/member/join) 경로로 들어올 파라미터를 설정해준다. http 요청 메시지를 설정해주는 것!
4. resultAction을 이용해 예상된 http 응답 결과를 검증한다.

	@Test
    @DisplayName("일반 회원가입 성공")
    void joinSuccess() throws Exception {
        // Given
        JoinRequestDto joinRequestDto = new JoinRequestDto();
        joinRequestDto.setUsername("test");
        joinRequestDto.setPassword("1234");
        joinRequestDto.setConfirmPassword("1234");
        joinRequestDto.setNickname("tester");
        joinRequestDto.setEmail("test@naver.com");

        Member member = Member.builder()
                .username("test")
                .password("1234")
                .nickname("tester")
                .email("test@naver.com")
                .userRole("free")
                .build();

        RsData<Member> joinRsData = new RsData<>("S-1", "%s님의 회원가입이 완료되었습니다.".formatted(joinRequestDto.getNickname()), member);

        doReturn(joinRsData).when(memberService).join(any(JoinRequestDto.class));
        doReturn("redirect:/member/login").when(rq).redirectWithMsg(any(String.class), any(RsData.class));

        // When
        ResultActions resultActions = mockMvc
                .perform(post("/member/join")
                        .contentType(MediaType.MULTIPART_FORM_DATA)
                        .param("username", "test")
                        .param("password", "1234")
                        .param("confirmPassword", "1234")
                        .param("nickname", "tester")
                        .param("email", "test@naver.com")
                )
                .andDo(MockMvcResultHandlers.print());

        // Then
        resultActions
                .andExpect(status().is3xxRedirection())
                .andExpect(handler().handlerType(MemberController.class))
                .andExpect(handler().methodName("join"))
                .andExpect(redirectedUrlPattern("/member/login/**"));

        verify(memberService, times(1)).join(any(JoinRequestDto.class));
        verify(rq, times(1)).redirectWithMsg(any(String.class), any(RsData.class));
    }
}

joinRequestDto, member, joinRsData는 Mock 객체인 MemerService, Rq에 가짜 값을 넣어주기 위해 생성한 객체이다.

doReturn(joinRsData).when(memberService).join(any(JoinRequestDto.class));
doReturn("redirect:/member/login").when(rq).redirectWithMsg(any(String.class), any(RsData.class));

이렇게 하면 memberService.join() 메서드를 사용할 때 반환 값으로 무조건 joinRsData를 받게된다.
rq.redirectWithMsg()의 경우, 회원가입이 성공할 경우 실행되는 redirect 경로가 반환되도록 했다.

// When
ResultActions resultActions = mockMvc
		.perform(post("/member/join")
		.contentType(MediaType.MULTIPART_FORM_DATA)
		.param("username", "test")
		.param("password", "1234")
		.param("confirmPassword", "1234")
		.param("nickname", "tester")
		.param("email", "test@naver.com")
		)
		.andDo(MockMvcResultHandlers.print());

mockMvc를 사용하면 Http요청, 응답을 테스트할 수 있다!
나는 회원가입 요청으로 받을 파라미터들을 설정해줬다. SSR 프로젝트기 때문에 formData를 사용했다. 따로 프론트엔드 프레임워크를 사용한다면 json 형태로 보내면 된다.

        // Then
        resultActions
                .andExpect(status().is3xxRedirection())
                .andExpect(handler().handlerType(MemberController.class))
                .andExpect(handler().methodName("join"))
                .andExpect(redirectedUrlPattern("/member/login/**"));

        verify(memberService, times(1)).join(any(JoinRequestDto.class));
        verify(rq, times(1)).redirectWithMsg(any(String.class), any(RsData.class));
    }
}

.andExpect()를 통해 http 응답을 검증할 수 있다.
redirect 요청인지 확인하고, MemberController의 join 메서드에서 요청을 잘 처리했는가 확인하는 코드이다. 검증 완료 후
verify() 코드를 통해 mock 객체들이 호출된 횟수를 비교해서 회원가입 성공 유닛 테스트를 마쳤다!

참고:
https://mangkyu.tistory.com/143
https://ws-pace.tistory.com/92
https://thalals.tistory.com/273

0개의 댓글