책 내용대로 가기에는 책 내용이 너무 함축적인 내용만 담고 있어서 실제적인 단물쪽쪽은 거의 없음.
책에서는 리팩토링 하는 코드가 제대로 돌아가는지 검증하는 용도로 쓰이는 것 같음.
어떤 내용이 좋을까 생각해서 다음의 경우를 생각해봄
책내용에서는 Mocha, 나는 Junit5를 이용해서 테스트
회원가입 로직 작성해보기
@Data
public class UserEntity {
private int seq;
private String id;
private String password;
private String name;
}
public interface UserRepository {
boolean existUserById(String userId);
UserEntity save(UserEntity userEntity);
}
그럼 이제 TDD 기본인 회원가입을 하는 테스트 코드로 설계를 해보자
public interface UserService {
void addUser(UserEntity.VO userVO);
}
@Service
public class UserServiceImpl implements UserService{
@Override
public void addUser(UserEntity.VO userVO) {
}
}
Fixture를 설정하자.
userId : test1
password : test1234
name : 테스트유저
테스트 코드를 작성하자
@SpringBootTest
public class UserServiceTest {
@InjectMocks
private final UserService userService = new UserServiceImpl();
private UserEntity.VO testUser;
@BeforeEach
public void setUp(){
UserEntity.VO.builder()
.id("test1")
.password("test1234")
.name("테스트 유저");
}
@Test
public void testAddUser(){
userService.addUser(testUser);
}
}
Param 체크가 올바로 되어야한다. (실패난 테스트 생각해서 짜기)
//경계값 혹은 정합성에 어긋난 실패난 경우
@Service
public class UserServiceImpl implements UserService {
private final Pattern pattern = Pattern.compile("[a-z|A-Z]{1}[a-z|A-Z]*[0-9]+[a-z|A-Z|0-9]*");
@Override
public void addUser(UserVO userVO) {
boolean isParamValid = StringUtils.isNotBlank(userVO.getId()) &&
StringUtils.isNotBlank(userVO.getName()) &&
StringUtils.isNotBlank(userVO.getPassword());
isParamValid = isParamValid &&
StringUtils.length(userVO.getPassword()) >= 6
&& userVO.getPassword().matches(pattern.pattern());
if(!isParamValid){
throw new RuntimeException("[회원가입 에러] 정합성 오류");
}
}
}
package com.example.test5.Service;
import com.example.test5.Model.Entity.UserEntity;
import com.example.test5.Model.VO.UserVO;
import com.example.test5.Service.Impl.UserServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class UserServiceTest {
@InjectMocks
private final UserService userService = new UserServiceImpl();
private UserVO testUser;
@Test
@DisplayName("아이디가 Null일떄")
public void testAddUserWhenIdNull() {
//fixture값 재수정
testUser.setId(null);
Assertions.assertThrows(RuntimeException.class, () -> {
userService.addUser(testUser);
});
}
@Test
@DisplayName("비밀번호가 이상한거일떄")
public void testAddUserWhenNotInvalidPassword(){
testUser.setPassword("sadklasdjaskl");
Assertions.assertThrows(RuntimeException.class, () -> {
userService.addUser(testUser);
});
}
}
@Override
public void addUser(UserVO userVO) {
boolean isParamValid = StringUtils.isNotBlank(userVO.getId()) &&
StringUtils.isNotBlank(userVO.getName()) &&
StringUtils.isNotBlank(userVO.getPassword());
isParamValid = isParamValid &&
StringUtils.length(userVO.getPassword()) >= 6
&& userVO.getPassword().matches(pattern.pattern());
if(!isParamValid){
throw new RuntimeException("[회원가입 에러] 정합성 오류");
}
if(userRepository.existUserById(userVO.getId())){
throw new RuntimeException("[회원가입 에러] 중복된 유저 아이디");
}
UserEntity userEntity = UserEntity.builder()
.id(userVO.getId())
.name(userVO.getName())
.password(userVO.getPassword())
.build();
userRepository.save(userEntity);
}
@Test
public void testAddUser() {
when(userRepository.existUserById(testUser.getId())).thenReturn(false);
userService.addUser(testUser);
verify(userRepository, times(1)).existUserById(testUser.getId());
}
@Test
@DisplayName("DB에 중복된 ID가 존재할 떄")
public void testAddUserWhenDuplicatedId(){
when(userRepository.existUserById(testUser.getId())).thenReturn(true);
Assertions.assertThrows(RuntimeException.class, () -> {
userService.addUser(testUser);
});
verify(userRepository, times(1)).existUserById(testUser.getId());
}
@Override
public void addUser(UserVO userVO) {
boolean isParamValid = StringUtils.isNotBlank(userVO.getId()) &&
StringUtils.isNotBlank(userVO.getName()) &&
StringUtils.isNotBlank(userVO.getPassword());
isParamValid = isParamValid &&
StringUtils.length(userVO.getPassword()) >= 6
&& userVO.getPassword().matches(pattern.pattern());
if(!isParamValid){
throw new RuntimeException("[회원가입 에러] 정합성 오류");
}
if(userRepository.existUserById(userVO.getId())){
throw new RuntimeException("[회원가입 에러] 중복된 유저 아이디");
}
UserEntity userEntity = UserEntity.builder()
.id(userVO.getId())
.name(userVO.getName())
.password(userVO.getPassword())
.build();
userRepository.save(userEntity);
}
@Test
public void testAddUser() {
when(userRepository.existUserById(testUser.getId())).thenReturn(false);
userService.addUser(testUser);
verify(userRepository, times(1)).existUserById(testUser.getId());
verify(userRepository, times(1)).save(any(UserEntity.class));
}
테스트 수정하기 (좀더 완벽하게)
에러가 뜨면 save가 실행이 되나 안되나, 중간 중간 로직들이 실행이 되나 안되나, 코드가 어디서 멈추는지 테스트 코드를 구체화하자.
@SpringBootTest
public class UserServiceTest {
@InjectMocks
private UserServiceImpl userService;
@Mock
private UserRepository userRepository;
private UserVO testUser;
@BeforeEach
public void setUp() {
testUser = UserVO.builder()
.id("test1")
.password("test1234")
.name("테스트 유저").build();
}
@Test
public void testAddUser() {
when(userRepository.existUserById(testUser.getId())).thenReturn(false);
userService.addUser(testUser);
verify(userRepository, times(1)).existUserById(testUser.getId());
verify(userRepository, times(1)).save(any(UserEntity.class));
}
@Test
@DisplayName("아이디가 Null일떄")
public void testAddUserWhenIdNull() {
//fixture값 재수정
testUser.setId(null);
Assertions.assertThrows(RuntimeException.class, () -> {
userService.addUser(testUser);
});
verify(userRepository, never()).existUserById(testUser.getId());
}
@Test
@DisplayName("비밀번호가 이상한거일떄")
public void testAddUserWhenNotInvalidPassword(){
testUser.setPassword("sadklasdjaskl");
Assertions.assertThrows(RuntimeException.class, () -> {
userService.addUser(testUser);
});
verify(userRepository, never()).existUserById(testUser.getId());
}
@Test
@DisplayName("DB에 중복된 ID가 존재할 떄")
public void testAddUserWhenDuplicatedId(){
when(userRepository.existUserById(testUser.getId())).thenReturn(true);
Assertions.assertThrows(RuntimeException.class, () -> {
userService.addUser(testUser);
});
verify(userRepository, times(1)).existUserById(testUser.getId());
verify(userRepository, never()).save(any(UserEntity.class));
}
}
쓰고나니 코드는 많고 큰 내용은 없다. 그치만 테스트 중요하니 화이링
암튼 끝