실제로 시큐리티가 findByUsername을 사용한다.
비밀번호를 체크하지 않는 이유는 자기가 체크할거니까 직접 하지 말라는 것이다.
내부적으로 비밀번호를 체크한다.
해시로 변경하는게 레파지토리의 책임은 아니지만
만약 다른 메서드에서 오류날 수 있기 때문에 해시로 바꿔야할 때가 있을 수 있다.
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@DataJpaTest
public class UserRepositoryTest {
// 아무것도 의존하는게 없기 때문에 stub이 필요없음
@Autowired
private UserRepository userRepository; // @DataJpaTest가 메모리에 띄워줌
@Test
@Order(1)
public void save_테스트() {
// given
String username = "ssar";
String password = "1234"; // 해시로 암호화하는것은 서비스 책임
String email = "ssar@nate.com";
LocalDateTime createDate = LocalDateTime.now();
LocalDateTime updateDate = LocalDateTime.now();
User user = new User(null, username, password, email, null, createDate, updateDate);
// when
User userEntity = userRepository.save(user);
// then
assertEquals(username, userEntity.getUsername());
}
@Test
@Order(2)
public void findByUsername_테스트() {
// given
String username = "ssar";
// when
Optional<User> userOp = userRepository.findByUsername(username);
if (userOp.isPresent()) {
User user = userOp.get();
// then
assertEquals(username, user.getUsername());
}
}
@Test
@Order(3)
public void findById_테스트() {
// given
Integer id = 1;
// when
Optional<User> userOp = userRepository.findById(id);
if (userOp.isPresent()) {
User userEntity = userOp.get();
// then
assertEquals(id, userEntity.getUsername());
}
}
@Test
@Order(4)
public void findByUsernameAndEmail_테스트() {
// given
String username = "ssar";
String email = "ssar@nate.com";
// when
Optional<User> userOp = userRepository.findByUsernameAndEmail(username, email);
if (userOp.isPresent()) {
User userEntity = userOp.get();
// then
assertEquals(username, userEntity.getUsername());
assertEquals(email, userEntity.getEmail());
}
}
}
given, when, then 정의하는 것 : BDD Mockito
BDD (Behavior-Driven Development)
given, when, then 구조의 패턴
스프링은 DI를 지원해주기 때문에 의존성 주입을 신경쓰지않고 객체간 의존 관계만 고민해서 설계하면 된다.
근데 의존성은 테스트 시점에 문제를 발생시킨다.
초기에는 시간이 좀 걸리겠지만 장기적으로 봤을때는 테스트를 하는게 더 좋다.
모키토는 의존성 주입을 쉽게 해주는 라이브러리
제이유닛은 단위 테스트 도구
하나하나 디비에 들어가는 건 레파지토리 일이고,
이런 디비 작업이 이어지는 트랜잭션을 서비스에서 테스트해야한다.
스텁 : 의존성 분리
given도 2개 stub도 2개 만들고나서 테스트해보면 nullpointerexception이 발생한다.
로그를 따라가보니 비크립트가 메모리에 뜨지 않았기 때문이다.
모크 컨테이터로 비크립트를 데려와야겠다.
@Mock
private BCryptPasswordEncoder passwordEncoder;
bcyrpt.encode( )에 대한 행동 정의가 없기 때문에 null 이 들어왔다,
근데 내가 얘 행동정의를 할 필요가 있을까?
얘가 레파지토리처럼 무거운가? 그냥 클래스 하나 메모리에 띄우는 것이다.
이걸 굳이 모크로 띄워서 stub 행동정의를 할 필요가 없다.
이때 붙여주는 어노테이션이 @Spy 이다.
모크 컨테이너에 스파이를 심는것이다,
심을 때는 바로 new 해서 넣어주면 된다.
@Spy
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
@ExtendWith(MockitoExtension.class) // Mockito 컨테이너 생성
public class UserServiceTest {
@InjectMocks
private UserService userService;
@Spy
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
@Mock
private UserRepository userRepository;
@Mock
private VisitRepository visitRepository;
// findByUsername을 호출하는 stub을 정의할건데 이건 테스트 필요없겠다. 레파지토리에서 테스트해라.
public void 유저네임중복체크() {}
@Test
public void 회원가입_테스트() {
// given 1
User givenUser = User.builder()
.username("ssar")
.password("1234")
.email("ssar@nate.com")
.build();
// stub 1
User mockUserEntity = User.builder()
.id(1)
.username("ssar")
.password("1234")
.email("ssar@nate.com")
.profileImg(null)
.createDate(LocalDateTime.now())
.updateDate(LocalDateTime.now())
.build();
Mockito.when(userRepository.save(givenUser)).thenReturn(mockUserEntity);
// given 2
Visit givenVisit = Visit.builder()
.totalCount(0L)
.user(mockUserEntity)
.build();
// stub 2
Visit visitEntity = Visit.builder()
.id(1)
.totalCount(0L)
.user(mockUserEntity) // 연결
.createDate(LocalDateTime.now())
.updateDate(LocalDateTime.now())
.build();
Mockito.when(visitRepository.save(givenVisit)).thenReturn(visitEntity);
// when 실세 서비스 호출
User userEntity = userService.회원가입(givenUser);
// then
assertEquals(givenUser.getEmail(), userEntity.getEmail());
}
public void 프로필사진수정하기_테스트() {}
public void 패스워드초기화() {}
}
본코드짜고 테스트 코드 짜다가 리팩토링을 했다.
1. visit 빌드패턴 정의 (@AllArgsConstructor 삭제!)
@Builder
public Visit(Integer id, Long totalCount, User user, LocalDateTime createDate, LocalDateTime updateDate) {
this.id = id;
this.totalCount = totalCount;
this.user = user;
this.createDate = createDate;
this.updateDate = updateDate;
}
2. userService에서 회원가입 메서드에 리턴타입 바꿔주기
@Transactional
public User 회원가입(User user) {
return userEntity;
}
다음시간에 컨트롤러 전부 테스트하고나면 RestDoc 라이브러리를 사용하여 API 문서를 만들어보자.
이 문서를 만들기 위한 핵심은 테스트 파일이 있어야한다. 없으면 안나온다!!
[출처]
https://cafe.naver.com/metacoding
메타 코딩 유튜브
https://www.youtube.com/c/%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9