팀 프로젝트에서 테스트 코드를 작성해볼 기회가 있었다.
테스트 코드를 작성하기 위해 본 코드의 클래스에서 ctrl + shift + t 를 눌러 클래스 내부의 메서드 중 테스트 코드를 작성하고자 하는 메서드를 선택해주었다.
위의 방법대로 테스트 클래스를 만들고 막막함이 컸다. 테스트 코드를 작성해본 적도 없고 어떻게 작성해야 하는지 방향을 잡기도 어려웠다. 그러나 테스트 코드를 작성하기 위해 가짜 Mock 객체를 만든다는 것을 알고있었고 여기서 말하는 Mock 객체란 내가 테스트하길 원하는 필요한 기능만으로 만들어둔 객체이다.
그리고 테스트 코드를 작성하며 권장되는 구조가 있는데 바로 given - when - then 구조이다.
이 구조는 테스트의 목적을 명확하게 표현해낼 수 있으며 봤을 때 이해하기도 편하다. 위의 구조에 대해 조금 더 자세히 말해보자면 아래와 같고 코드도 같이 보겠다.
1. given - 테스트 대상 코드가 어떤 입력값을 받아들이는지 그리고 입력값이 어떤 상황을 나타내는지를 표현해주면 좋다.
- 예를 들어 회원가입 기능에대한 테스트라고 생각한다면 웹에서 아이디, 비밀번호 연락처 등을 입력해줄 것이다. 하지만 테스트코드의 given 구역에는 값을 직접 입력해주는 과정이 필요하다.
2. when - 실제 테스트 대상 코드를 실행한다.
3. then - 예상되는 결과를 검증한다.
- mock 객체를 이용하여 어떤 메소드가 호출되었는지, 몇 번 호출되었는지, 어떤 매개변수가 전달되었는지 등
아래의 테스트 코드는 혼자서 given when then을 구분하여 작성해본 코드이다.
package com.example.lablink.user.service;
import com.example.lablink.user.dto.request.SignupRequestDto;
import com.example.lablink.user.entity.Terms;
import com.example.lablink.user.entity.User;
import com.example.lablink.user.entity.UserInfo;
import com.example.lablink.user.entity.UserRoleEnum;
import com.example.lablink.user.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.times;
@SpringBootTest
class UserServiceTest {
// 임시의 Mock 객체를 생성할 때 import가 자동으로해당 service안에 있는 클래스들의 import? 의존성을 알아서 주입해준다 ??
// 테스트 코드를 작성할 Service 주입.
// setup메서드를 작성하지 않아도 된다
// setup메서드는 Mock객체를 인스턴스화 시키는 것 ?
@InjectMocks
private UserService userService;
// Mock으로 UserService에서 사용되는 클래스를 의존성 주입함.
// 테스트에서는 UserService의 JwtUtill과 같은 클래스를 주입받을 필요는 없다.
@Mock
private UserRepository userRepository;
@Mock
private PasswordEncoder passwordEncoder;
@Mock
private UserInfoService userInfoService;
@Mock
private TermsService termsService;
@Test
void signup() {
// given -> 기존 코드의 경우 클라이언트에서 데이터를 받아오지만 테스트에서는 생성자를 통해 지정함.
SignupRequestDto signupRequestDto = new SignupRequestDto(
"test01@naver.com",
"encodePassword",
"010-1111-1111",
true,
true,
true,
true,
false
);
User usersaved = new User(signupRequestDto, passwordEncoder.encode(signupRequestDto.getPassword()), new UserInfo(), UserRoleEnum.USER);
// 아래의 userRepository.existsByEmail() 메서드 호출시 false반환 설정 -> 이메일 중복 X
// 아래의 given을 해주는 이유는 원본 UserService에서 호출되는 메서드가
given(userRepository.existsByEmail(signupRequestDto.getEmail())).willReturn(false);
given(passwordEncoder.encode(signupRequestDto.getPassword())).willReturn("encodePassword");
given(userInfoService.saveUserInfo(signupRequestDto)).willReturn(new UserInfo());
given(userRepository.save(any(User.class))).willReturn(usersaved);
given(termsService.saveTerms(signupRequestDto, usersaved)).willReturn(new Terms());
// when -> 메서드를 변수화? -> then의 코드 실행에서 when을 사용한다.
// UserService에서 실행할 메서드(테스트할 메서드)를 변수에 담기?
String result = userService.signup(signupRequestDto);
// then -> when 몇번 실행할지,
// 검증 단계 -> 호출할 특정 메서드 지정 가능 및 횟수 설정 가능.
then(userRepository).should(times(1)).existsByEmail(signupRequestDto.getEmail());
then(passwordEncoder).should(times(2)).encode(signupRequestDto.getPassword());
then(userInfoService).should(times(1)).saveUserInfo(signupRequestDto);
then(userRepository).should(times(1)).save(any(User.class));
then(termsService).should(times(1)).saveTerms(signupRequestDto, usersaved);
}
}
위와 같이 회원가입 기능에대해 테스트 코드를 작성했지만 한 가지 의문이 있다. 이메일 중복, 빈값 입력, 형식오류 등 회원가입을 하는 과정에서 각각의 상황에 맞는 코드가 많을 것이라 예상되는데, 전부다 위와 같은 형식으로 길게 작성해야 하는지 개선할 방법은 없는지 궁금해졌다.
개선 방법이나 다른 방법의 테스트코드 작성에 대해서는 조금 더 공부를 하고 작성하도록 하고 mockmvc에대해 간단히 말하고 끝내려고 한다.
일단 mockmvc도 테스트코드를 작성 하는 것 중에 하나이다. 위에서 만들어본 테스트 코드는 본 코드에 정의되어있는 회원가입 메서드를 http 요청을 보내지 않고 단순히 호출해 테스트해보는 것이고, http요청을 보내는 테스트 코드가 따로 있다고 하는데 그것이 바로 mockmvc이다. 다음시간에는 위의 테스트 코드에대한 피드백과 mockmvc에대해 조금 더 다뤄볼 것이다.
'TIL' 카테고리의 다른 글
코드 리팩토링 (0) | 2023.05.01 |
---|---|
Redis의 문제점 (0) | 2023.04.25 |
웹 사이트 CSRF공격과 XSS 공격 (0) | 2023.04.10 |
포스트맨 이용한 카카오톡 로그인 테스트 - OAuth2 (0) | 2023.03.30 |
단위 테스트를 사용해야 하는 이유 - TDD (0) | 2023.03.29 |