Mock Test
UserServiceTest
가짜 객체는 동작을 하지 않는다. (passwordEncoder의 encode가 null)
처음에 test 코드를 작성했을 때 회원가입 로직에서 인코딩 된 pw와 인코딩 되지 않은 pw를 비교하여 검증을 하려고 했다.
하지만 encode된 pw가 제대로 값을 띄우지 못하고 계속 null이 떴고 그 이유를 알지 못했다.
아래 코드는 인코딩이 제대로 되지 않아서 user에 그냥 password 그 자체의 값을 넣어줬고
String pw = passwordEncoder.encode(signUpRequestDto.getPassword());
값을 출력해보면 null로 출력되었다.
문제가 된 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class UserServiceTest {
@InjectMocks
SignServiceImpl userService;
@Mock
UserRepository userRepository;
@Mock
PasswordEncoder passwordEncoder;
User user;
@Test
@DisplayName("회원가입 test")
void signUp() {
//given
SignUpRequestDto signUpRequestDto = new SignUpRequestDto();
signUpRequestDto.setNickname("끼까꿍");
signUpRequestDto.setPassword("뮤뮤");
signUpRequestDto.setName("김철수");
List<String> role = Collections.singletonList("ROLE_USER");
String pw = passwordEncoder.encode(signUpRequestDto.getPassword());
user = User.builder()
.nickname(signUpRequestDto.getNickname())
.name(signUpRequestDto.getName())
.password(signUpRequestDto.getPassword())
.roles(role)
.build();
when(userRepository.save(any(User.class))).thenReturn(user);
//when
userService.signUp(signUpRequestDto);
verify(userRepository).save(any(User.class));
//then
assertAll( // 2가지 test
() -> assertEquals(signUpRequestDto.getNickname(), user.getNickname()),
() -> assertEquals(signUpRequestDto.getName(), user.getName())
);
}
원인
@Mock
은 가짜 객체이다.
가짜 객체는 실제 서비스에 맞게 쓰려고 하면 NULL로 뜬다. → 가짜 객체는 동작을 안한다.
실제 객체를 가지고 쓴 다음 stubbing을 통해서 가짜 객체를 사용한 다음 retnrun 값을 실제 객체로 넣어줘야한다.
요약
PasswordEncoder passEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
: 실제 객체 만들기
String pw = passEncoder.encode(signUpRequestDto.getPassword());
: 실제 객체로 encoding하기
when(passwordEncoder.encode(user.getPassword())).thenReturn(pw);
: 가짜 객체에 실제 객체를 return한다.
(가짜 객체로 실행했을 때 이렇게 동작해라 라고 알려줘야 한다.)
전체 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Test
@DisplayName("회원가입 test")
void signUp() {
//given
PasswordEncoder passEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); // 1번
SignUpRequestDto signUpRequestDto = new SignUpRequestDto();
signUpRequestDto.setNickname("끼까꿍");
signUpRequestDto.setPassword("뮤뮤");
signUpRequestDto.setName("김철수");
List<String> role = Collections.singletonList("ROLE_USER");
String pw = passEncoder.encode(signUpRequestDto.getPassword()); // 2번
user = User.builder()
.nickname(signUpRequestDto.getNickname())
.name(signUpRequestDto.getName())
.password(pw)
.roles(role)
.build();
when(userRepository.save(any(User.class))).thenReturn(user);
//when
userService.signUp(signUpRequestDto);
verify(userRepository).save(any(User.class));
// then
assertFalse(user.getName().isEmpty()); // 3번
}
1번. 실제 객체
PasswordEncoder 설정 파일로 가면 아래와 같이 정의했다.
1
2
3
4
5
6
7
@Configuration
public class PasswordEncoderConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
여기서 return값을 복사한 다음 실제 객체로 test코드에 작성해준다.
2번. password 인코딩
passEncoder.encode(signUpRequestDto.getPassword());
실제 객체에 encoding 후 User 객체에 값을 넣어준다.
3번. 검증
UserService 로직에서 name이 비어있는지 체크하는 코드가 작성되어있다.
해당 부분을 체크하고 마무리하였다.
참고로 test하려고 하는 로직에 없는 로직을 작성할 경우
*Please remove unnecessary stubbings or use ‘lenient’ strictness. 와 같은 에러가 뜰 수 있다.
이 부분은 아래 예시로 참고한다.
RegistryServiceTest
코드에 없는 로직을 test 코드에 작성하지 않는다.
RegistryService.java
1
2
3
4
5
6
public PagingResult getBoards(int curPage) {
Pageable pageable = PageRequest.of(curPage - 1, PAGE_POST_COUNT);
Page<Registry> boards = registryRepository.findAllByOrderByCreatedAtDesc(pageable);// 생성 날짜 순으로 보여주기
List<Registry> boardList = boards.getContent(); // 조회된 데이터
// 코드 생략
}
위 코드는 db에서 생성 날짜 순으로 페이징 된 결과를 출력시킨다.
위 로직에는 repository에 저장하는 코드가 없다.
그런데 test 코드에 객체를 저장하는 코드를 작성하면 안된다는 얘기다.
RegistryServiceTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void paging() {
User user = User.builder()
.nickname("nickname")
.password("123456")
.build();
Registry registry = Registry.builder()
.title("test")
.main("main")
.user(user)
.build();
// when(registryRepository.save(any())).thenReturn(registry); // 실제 코드에 없어서 기각
// 이후 코드 생략