MemberRepository와 MemberService가 각각 있다고 가정하자.
//MemberService
package com.yourecipe.member.service;
import com.yourecipe.member.model.Member;
import com.yourecipe.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor // 생성자 주입
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
@Override
public boolean signUpMember(Member member) {
return memberRepository.joinMember(member) > 0;
}
}
//MemberRepository
package com.yourecipe.member.repository;
import com.yourecipe.member.model.Member;
import org.springframework.stereotype.Repository;
@Repository
public interface MemberRepository {
// 회원정보 등록하기
int joinMember(Member member);
// 회원정보 가져오기
Member findMember(int memberId);
// 회원정보 수정하기
int updateMember(Member member);
// 회원정보 삭제하기
int quitMember(int memberId);
}
통합 테스트가 아닌 서비스 로직에 대해서만 단위 테스트를 하기 위해서는 Repository나 Controller에 모두 독립적이어야 한다.
따라서, 단위테스트를 작성할 때는 @Mock 애노테이션을 이용해서 서비스 코드에 의존성 주입을 해줘야 하는 빈 객체들을 가짜 객체로 만들어준다.
@Mock
private MemberRepository memberRepository;
그리고 실제로 주입받고자 하는 서비스 코드는 @InjectMocks 애노테이션을 이용해 Mock으로 만들어진 가짜 객체들을 주입해준다.
반드시 Service 구현체를 의존성 주입해줄 객체로 설정해야 한다. 만약 Service interface를 주입 대상으로 설정하면 테스트 오류가 발생한다.
@InjectMocks
private MemberServiceImpl memberService;
단위 테스트는 given, when, then 순서로 구성하는 것이 바람직하다.
Given(테스트 사전 조건이 주어짐), When(실제 테스트가 진행됨), Then(테스트 결과를 알려줌) 순서로 나는 간단하게 이해하고 작성중이다.
@Test
@DisplayName("회원 서비스 : 회원 가입 테스트(성공)")
void 회원가입_성공() {
//given
given(memberRepository.joinMember(any())).willReturn(1);
Member member = createMemberForTest();
//when
boolean result = memberService.signUpMember(member);
//then
assertThat(result).isEqualTo(true);
}
// 테스트에 사용할 member 객체 생성
private Member createMemberForTest() {
return Member.builder()
.memberId(1)
.email("zayson.maeng@gmail.com")
.nickname("zayson")
.profileImg("test.com")
.build();
}
라인 별로 코드를 알아보면, given(Object MethodCall) 메소드는 `import static org.mockito.BDDMockito.given;` 해당 라이브러리에서 제공되는 메소드로 의존성 주입된 객체 , 즉 "대체하는 객체가 호출한 메소드가 주어진다면" 으로 해석하면 된다. 위 테스트 코드에서는 MemberService에 대한 코드를 테스트하는 것이지 MemberRepository를 테스트 하는 것이 아니기 때문에 Repository 객체 ( Mock 객체 )의 메소드를 호출하고, 호출하는 메소드의 객체도 서비스 로직 테스트에서는 의존하지 않으므로 any() 메소드로 작성해주었다.
마지막으로, willReturn() 메소드를 통해 Repository가 호출 후 어떤 값을 결과로 줄 지에 대해 작성한다.
when 부분에서는 실제 테스트할 서비스 로직을 호출한다. memberService의 signUpMember 메소드를 호출해 결과값을 저장한다. 실제 서비스 코드는 Repository의 joinMember 메소드를 호출 후 결과값이 양수라면 true를 던져주는 로직이다.
따라서, Then 부분에서 1값을 가져오고 true를 결과로 저장할 것이기 때문에 assertThat 메소드로 실제 결과와 예상 결과를 비교해주면 된다.
💡 테스트 코드를 작성할 때는 반드시 성공하는 테스트만 작성하는 것이 아닌 실패하는 테스트 코드도 작성하는 습관을 들이자.
https://jiminidaddy.github.io/dev/2021/05/20/dev-spring-단위테스트-Repository/