본문 바로가기
Spring

[Spring] TDD 도입기(2) feat) Service 계층

by windy7271 2024. 12. 22.
728x90
반응형

 

이번에는 Servie 계층을 구현하려고 합니다.

 

우리 화면에서 이메일과, Role 을 헤더 부분에 보여주려고 합니다.

앞에서 레포지토리 계층을 개발했던 것처럼 이번에도 서비스 계층에 대한 테스트 클래스부터 작성해 주면 됩니다..

 

서비스 테스트 만들기

 

맥북 기준 cmd + shift + t 입니다.

 

근데 원래는 서비스 클래스를 만드는것보다 서비스 테스트코드를 먼저 짜야하기 때문에 순서가 바뀌었지만.

클래스명만 만들고 만드는것도 나쁘지 않은거 같습니다.  왜냐하면 폴더 위치까지 다 알아서 만들어 주기 때문이죠

 

서비스 계층은 데이터 베이스에 데이터를 처리하는 Repo 계층을 모킹하기 위해 MockitoExtension 에서 실행 되도록 합니달.

 

Mockito 도 테스팅 프로임 워크이기 떄문에 JUnit 과 결합되기 위해서는 별도의 작업 이 필요한대 그것이 바로 

@ExtendWith(MockitoExtension.class)

이거를 사용해주면 됩니다

 

그러면 예를들어 컨트롤단에서는 서비스단이 주입이 된다거나, 서비스단에서는 레포가 주입이 됩니다.

 

memberService 는 테스트 대상이므로 의존성이 주입되는 어노테이션인 @InjectMocks를 붙여주고, MemberRepository 가 의존성이 있는 클래스 이므로 가짜 객체 생성을 도와주는 @Mock을 붙여줍니다

 

@ExtendWith(MockitoExtension.class)
class MemberServiceTest {
    @InjectMocks
    private MemberService memberService;

    @Mock
    private MemberRepository memberRepository;
}

이제 멤버의 email과 role을 가져오는 코드를 작성하면 되는데, 실패하는 테스트 코드 들을 먼저 작성 합니다.

 

1. 내 정보를 가져오기 전에 해당 멤버가 존재하는지 여부 체크한다.

 

@ExtendWith(MockitoExtension.class)
public class MemberServiceTest {
    @InjectMocks
    private MemberService memberService;

    @Mock
    private MemberRepository memberRepository;

    @Test
    @DisplayName("getMember: ErrorCase1. 해당 멤버를 찾지 못할 때")
    void test1(){
        // given
        doReturn(Optional.empty()).when(memberRepository).findByEmail(any(String.class));
        // when
        IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> memberService.getMyEmailAndRole("test@test.com"));
        //then
        assertThat(ex.getMessage()).isEqualTo("Account cannot be found");
    }
    public Member getMember(){
        return Member.builder()
                .email("test@test.com")
                .role(Role.GOLD)
                .build();
    }
}

 

doreturn을 사용하게 되면

findbyemail 을 해서 아무 스트링값을 넣으면 enpty값을 반환 해준다는 것입니다.

 

이렇게 되면 해당하는 계정이 없어 테스트는 통과하게 된다. 다음으로는 해당 멤버를 찾았을때 테스트 코드를 작성해줍니다.

 

 @Test
    @DisplayName("getMyEmailAndRole(): Success")
    void test2() {
        // given
        Member mockMember = getMember(); // 동일 객체 사용

        doReturn(Optional.of(mockMember)).when(memberRepository).findByEmail(any(String.class));
        doReturn(new LoginDataResDto("test@test.com", "GOLD"))
                .when(typeChange).MemberToMyEmailAndRoleDto(mockMember); // 동등성 비교

        // when
        LoginDataResDto result = memberService.getMyEmailAndRole("test@test.com");

        // then
        assertThat(result.email()).isNotNull();
        assertThat(result.role()).isEqualTo(Role.GOLD.name());

        // verify
        verify(memberRepository, times(1)).findByEmail("test@test.com");
        verify(typeChange, times(1)).MemberToMyEmailAndRoleDto(mockMember);
    }

 

TypeChange 라는 클래스가 따로 필요하기 때문에 그것또한 @Mock 으로 해줍니다..

 

 

doReturn(...).when(...)

  1. doReturn(returnValue):
    • 메서드 호출 시 반환될 값을 정의합니다.
    • 여기서 returnValue는 해당 메서드가 호출되었을 때 반환될 값입니다.
    • 이 방식은 일반적으로 when(...).thenReturn(...)과 같은 역할을 합니다
  2. .when(mockObject):
    • Mock 객체를 지정합니다.
    • 여기서 지정된 Mock 객체의 특정 메서드가 호출될 때 반환 동작을 정의합니다.
  3. .methodCall(arguments):
    • Mock 객체에서 호출할 메서드와 해당 메서드에 전달되는 매개변수를 지정합니다.

즉 지금 상황해서는 findbyemail 에 아무 string 값 넣으면 mockMember를 주고 membertoMyEmailAndRole 했을대 LoginDataResDto를 만들어 주는것입니다.

 

예전에 한 번 잠깐 배웠을때 때 AssertThat 까지는 봤는데요 이번에 verify 는 처음 봅니다.

 

Verify는 결과가 아니라 조작한 동작을 확인하고 싶은 경우 이용하는것입니다. 즉 몇번 호출했는지 알 수 있다고 보면 됩니다.

 

안에는

- times(n) : n번 실행 여부

- never() : 실행 안되어야함.

- atMostOnce() : 최대 1번 실행

- atLeastOnce() : 최소 1번 실행

- atLest(n) : 최소 n번 실행

- atMost(n) 최대 n번 실행

 

이런것들이 있다고 합니다.

 

이렇게 테스트 코드를 작성하고 나서 실제 서비스 단은

public LoginDataResDto getMyEmailAndRole(String email) {
    Member member = memberRepository.findByEmail(email).orElseThrow(
            () -> new IllegalArgumentException("Account cannot be found"));

    return typeChange.MemberToMyEmailAndRoleDto(member);
}

일단 다음과 같이 작성하게 됩니다.

 

다음에는 Controller 계층으로 돌아오겠습니다.

 

 

 

반응형

댓글