입사 후 경험들을 담으려 이 글을 시작으로 다시 블로그를 시작하려 한다
이 글부터는 OJT에서 Junit5을 사용한 경험을 적겠다
Junit이란 이름에서 유추할 수 있듯이 JVM을 사용하는 기반의 언어에서 테스트를 위해 사용하는 테스팅 프레임워크이다 @Test 어노테이션으로 테스트 메소드를 정의할 수 있으며 assert로 실제 값이 내가 기대한 값과 일치하는지 검증한다
Mock이란 진짜 객체가 아닌 가짜 객체로 진짜 객체의 동작을 모의로 테스트하기 위해 사용한다. 가령 테스트 하려는 소스가 구글 등 다른 플랫폼의 외부 API를 사용한다고 환경이라면 우리는 테스트 시 매번 외부와 통신하기보단 외부 서비스 없이도 테스트를 진행하도록 모의 객체를 만들어 테스트를 진행하기 위해 사용한다
아래 환경에서 given-when-then 패턴으로 테스트를 진행하려고 한다
StudyService
@RequiredArgsConstructor
@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class)
@Service
public class StudyService {
private final MemberService memberService;
private final StudyRepository studyRepository;
private final StudyMapper studyMapper;
public StudyResDTO createStudy(StudyReqDTO reqDTO){
try {
Optional<Member> member = memberService.findById(reqDTO.getOwner());
if (member.isEmpty()){
throw new IllegalArgumentException("There is no memberId " + reqDTO.getOwner());
}
Study study = studyMapper.toStudy(reqDTO);
studyRepository.save(study);
return studyMapper.toStudyResDTO(study);
} catch (Exception err){
err.printStackTrace();
}
return null;
}
}
StudyMapper
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR,
imports = {
Member.class
})
public interface StudyMapper {
StudyMapper INSTANCE = Mappers.getMapper(StudyMapper.class);
@Mapping(target = "limit", source = "chapter")
StudyResDTO toStudyResDTO(Study study);
@Mappings({
@Mapping(target = "owner", expression = "java(Member.builder().idx(dto.getOwner()).build())"),
@Mapping(target = "idx", ignore = true),
@Mapping(target = "chapter", source = "limit")
})
Study toStudy(StudyReqDTO dto);
}
MemberService
@RequiredArgsConstructor
@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class)
@Service
public class MemberService {
private final MemberRepository memberRepository;
public Optional<Member> findById(Long Id) {
return memberRepository.findById(Id);
}
}
StudyRepository
package me.tmdtjq32.myproject.src.repository;
import me.tmdtjq32.myproject.src.model.entity.Study;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface StudyRepository extends JpaRepository<Study,Long> {
}
StudyService에서 우리가 테스트 하려는 건 DB에서 값을 잘 불러오는지가 아니라 Option member가 정상적으로 응답했거나 empty인 경우를 mock을 통해 가정해 우리의 의도대로 코드가 동작하는지가 테스트의 목적이다
StudyServiceTest
@InjectMocks
StudyService studyService;
@Mock MemberService memberService;
@Mock StudyRepository studyRepository;
@Mock StudyMapper studyMapper;
@Test
@DisplayName("createStudy 성공 케이스")
void createStudy() {
// given
StudyReqDTO reqDTO = StudyReqDTO.builder()
.limit(1)
.name("study")
.status(StudyStatus.TODO)
.owner(1L)
.build();
Study study = Study.builder()
.chapter(reqDTO.getLimit())
.name(reqDTO.getName())
.status(reqDTO.getStatus())
.owner(Member.builder().idx(reqDTO.getOwner()).build())
.build();
StudyResDTO resDTO = StudyResDTO.builder()
.limit(study.getChapter())
.name(study.getName())
.status(study.getStatus())
.build();
Member member = Member.builder()
.idx(1L)
.name("test")
.email("test@email.com")
.build();
when(memberService.findById(reqDTO.getOwner()))
.thenReturn(Optional.of(member));
when(studyMapper.toStudy(reqDTO))
.thenReturn(study);
when(studyRepository.save(study))
.thenReturn(study);
when(studyMapper.toStudyResDTO(study))
.thenReturn(resDTO);
// when
StudyResDTO result = studyService.createStudy(reqDTO);
// then
assertAll(
() -> assertThat(result.getLimit()).as("limit가 일치하지 않습니다").isEqualTo(resDTO.getLimit()),
() -> assertThat(result.getName()).as("name이 일치하지 않습니다").isEqualTo(resDTO.getName()),
() -> assertThat(result.getStatus()).as("status가 일치하지 않습니다").isEqualTo(resDTO.getStatus())
);
}
아래와 같이 findById 메소드의 응답을 정의 후
when(memberService.findById(reqDTO.getOwner()))
.thenReturn(Optional.of(member));
createStudy 메소드를 실행했을 때
studyService.createStudy(reqDTO);
우리가 의도한 응답이 나오는지 검증하는 것이다
assertAll(
() -> assertThat(result.getLimit()).as("limit가 일치하지 않습니다").isEqualTo(resDTO.getLimit()),
() -> assertThat(result.getName()).as("name이 일치하지 않습니다").isEqualTo(resDTO.getName()),
() -> assertThat(result.getStatus()).as("status가 일치하지 않습니다").isEqualTo(resDTO.getStatus())
);