모르지 않다는 것은 아는것과 다르다.

Junit

Service 단위테스트

채마스 2022. 2. 27. 01:44

Service 단위테스트

  • Service 레이어만 단위테스트 하기위해서는 먼저 Controller와의 연결을 끊어야한다.
    • Controller는 Web모듈이므로 Service Test를 진행하려면 Web에 대한 의존성을 받으면 안된다.
    • @WebMvcTest, @SpringBootTest와 같은 테스트를 사용하면 Service만을 테스트하기가 어려워진다.
  • 두 번째로 Repository와의 연결을 끊어야 한다.
    • Domain을 통해 비즈니스 로직은 수행해야하지만, 실제로 DB에 저장할 건 아니기 때문에 이 부분을 제거할 방법이 필요하다.
    • SpringBoot 테스트는 특정 객체를 가짜로 대체할 Mocking을 제공하고 있고, 아래와 같은 Annotation을 제공한다.
      • @Mock
      • @MockBean
      • @Spy
      • @SpyBean
  • @ExtendWith(MockitoExtension.class) 을 추가하여 단위 테스트를 작성한다.
  • Service 내에 의존하고 있는 Repository를 @Mock으로 선언하면 Repository Bean에 의존하지 않고 테스트가 가능해진다.
  • Service 클래스를 @InjectMocks로 선언함으로써, @Mock으로 선언된 가짜 객체들을 의존한 Service 객체가 생성된다.
  • 예시는 아래와 같다.
@ExtendWith(MockitoExtension.class)
public class MemberServiceTest {
    @Mock
    private MemberRepository memberRepository;

    @InjectMocks
    private MemberService memberService;

    @Test
    @DisplayName("find 기능 확인")
    void find() {

        // given
        Member member = new Member("hyunwook", "서울");
        when(memberRepository.save(any())).thenReturn(member);
        memberService.join(member);  // service 로직
        when(memberRepository.findById(1L)).thenReturn(Optional.of(member));

        // when
        Member result = memberService.find(1L);

        // then
        Assertions.assertThat(result).isNotNull();
        Assertions.assertThat(result.getName()).isEqualTo("hyunwwok");
        Assertions.assertThat(result.getLocation()).isEqualTo("서울");
    }
}
  • @Mock는 대체할 모듈위에 붙이고 @InjectMocks는 테스트할 모듈에 붙인다. 그러면 @Mock라 붙은 모둘이 @InjectMocks가 붙은 모듈에 주입된다.
  • 위의 코드에서 save 메소드 실행시 실제 save 메소드는 작동하지 않고 그냥 바로 member를 돌려준다. 한마디로 save 메소드안에 아무리 복잡한 코드가 있어도 실행하지 않고 그냥 결과만을 return한다.

 

ArgumentCaptor

  • ArgumentCaptor 을 사용하여 특정 메소드에 사용되는 아규먼트를 저장해 놓았다가 다시 사용할 수 있다.
  • 쉽게 말해서, 아규먼트를 capture 했다가 getValue 로 사용한다.
@Mock
private DeveloperRepository developerRepository;

@InjectMocks
private DMakerService dMakerService;

@Test
void createDeveloperTest_success() {
    //given
    CreateDeveloper.Request request = CreateDeveloper.Request.builder()
            .developerLevel(JUNGNIOR)
            .developerSkillType(FRONT_END)
            .experienceYears(7)
            .memberId("memberId")
            .name("name")
            .age(28)
            .build();
    ArgumentCaptor<Developer> captor =
            ArgumentCaptor.forClass(Developer.class);

    //when
    CreateDeveloper.Response response = dMakerService.createDeveloper(request);

    //then
    verify(developerRepository, times(1))
            .save(captor.capture());
    Developer savedDeveloper = captor.getValue();
    assertEquals(JUNGNIOR, savedDeveloper.getDeveloperLevel());
    assertEquals(FRONT_END, savedDeveloper.getDeveloperSkillType());
    assertEquals(7, savedDeveloper.getExperienceYears());

    assertEquals(JUNGNIOR, response.getDeveloperLevel());
    assertEquals(FRONT_END, response.getDeveloperSkillType());
    assertEquals(7, response.getExperienceYears());
}
  • 위의 코드와 같이 vertify에 capture 를 전달하고 captor.getValue() 를 통해서 어떤 값이 전달됐는지 확인할 수 있다.




REFERENCES

'Junit' 카테고리의 다른 글

WebMvc Test  (0) 2022.02.27
단위테스트 vs 통합테스트  (0) 2022.02.27
Repository 단위테스트  (0) 2022.02.27
MockMvc  (0) 2022.02.27
Mockito  (0) 2022.02.27