Service 계층을 독립적으로 테스트를 해야 하는 이유

Kevin·2024년 6월 18일
4

Spring

목록 보기
20/27
post-thumbnail

🌹 서론

나는 대학생 동안 개발 공부를 하면서 쭉 품어오던 궁금증이 있었는데 그 중 하나가 아래의 궁금증이었다.

왜 Service 계층을 독립적으로 테스트를 해야할까?

위의 궁금증을 조금 더 풀어서 설명하자면 왜 영속성 계층과 함께 테스트를 진행하지 않냐는 궁금이었다.

Repository 계층, 즉 @DataJpaTest 어노테이션을 사용한 테스트와 함께 Service 단 또한 같이 테스트를 진행하면 안되는가? 라는 생각을 많이 가지고 있었다.

만약 두 계층을 한번에 테스트 한다면 생산성도 올라갈 것이고, 어차피 연관되어있는 두 계층을 한번에 테스트 한다고 하더라도 문제될 것은 없어보였기 때문이다.

내가 이번에 토이 프로젝트를 진행하면서 작성했던 코드들을 통해 왜 Service 계층을 독립적으로 테스트 해야 하는지에 대해서 깨달았는지 함께 알아보자.


🌷 본론

내가 왜 서론과 같은 생각을 하게 되었는지에 대해서 먼저 이야기 해보겠다.

지금에서야 내가 왜 그런 생각을 했는지를 알 수 있을 것 같다.

바로 그전까지 진행했던 프로젝트들에서는 Service 계층이 기본적인 CRUD에 그쳤기 때문이다.

@Service
@RequiredArgsConstructor
public class DailyLikeService {

    private final DailyLikeRepository dailyLikeRepository;

    @Transactional
    public void saveLike() {
    
    }
    
    @Transactional
    public void readrLike() {
    
    }
    
    @Transactional
    public void updateLike() {
    
    }
    
    @Transactional
    public void deleteLike() {
    
    }
}

일반적으로 위 코드와 같은 구조에서 크게 벗어난 비즈니스 로직을 작성 할 일이 없었기 때문에, Service 계층이 비즈니스 로직을 다루는 역할보다 Controller 계층과 Repository 계층을 이어주는 중간 다리 역할만 수행했기 때문이었다.

그랬기에 CRUD에 대해서 다루는 Repository 계층과 함께 테스트를 진행하면 되지 않을까? 라는 단순한 생각을 가지게 되었던 것이었다.

그렇다면 Service 계층을 독립적으로 수행 한다는 것은 어떤 뜻일까?

독립적으로 수행되어야 한다는 것의 의미를 알기 위해 마틴 엉클 밥이 이야기 한 F.I.R.S.T Unit Tests 에 대해서 먼저 알아보자.

F.I.R.S.T Unit Tests

F : Fast

이는 단위 테스트는 가능한 빠르게 실행 되어야 함을 의미하며 예를 들어 @SpringBootTest 와 같은 어노테이션은 Spring 애플리케이션의 모든 빈을 IoC Container에 등록한 후에 테스트를 진행 하기 때문에 테스트가 매우 느려진다.

I : Independent

단위 테스트는 현재 객체의 상태나 이전 테스트가 실행 되었던 결과, 다른 메서드들의 결과등에 의존 되면 안된다.

즉 단위테스트는 어떠한 순서로 실행 하더라도 성공을 하여야 한다.

이러한 독립성을 위해 일부에서는 @Order를 하기도 하는데 이는 각 테스트 간에 너무 의존적으로 변하게 만들기에 독립성을 반대로 해칠 수 있다.

또한 @Dirtiescontext 어노테이션을 통해 테스트간 격리를 하기도 하지만, 이는 테스트마다 컨텍스트 자체가 재생성 되기에 1번 Fast 원칙을 위반할 가능성이 크다.

그래서 일반적으로 data.sql 스크립트 파일에 더미 데이터를 미리 추가해두거나, TRUNCATE TABLE 쿼리를 작성한 스크립트를 두고 @Sql 어노테이션을 사용하는 방식을 사용한다.

R : Repetable

단위 테스트는 반복 가능해야 한다.

일반적으로 DB에 의존된 테스트는 delete나 insert 등의 구문으로 인해 여러번 실행 하는 경우에는 실패 가능성이 크다.

Self - Validating

단위 테스트는 사용자가 수동으로 결과를 확인할 필요 없이, Assert문 등을 통해서 성공 여부를 바로 결과로 확인할 수 있어야 한다.

Timely

단위 테스트를 통과하는 제품 코드가 작성되기 바로 전에 단위 테스트를 작성 해야 한다는 뜻인데, 이는 TDD를 적용할 때 함양되는 말로 이해했다.

아래 코드는 위의 규칙들을 최대한 준수하며 작성하기 위해 노력했다.


@ExtendWith(MockitoExtension.class)
public class DailyServiceTest {

    @InjectMocks
    private DailyService dailyService;

    @Mock
    private DailyRepository dailyRepository;

    @Mock
    private DailyLikeRepository dailyLikeRepository;

    @Mock
    private DailyContentRepository dailyContentRepository;

    @Test
    public void get_dailies() {
        // given
        List<Daily> dailies = MockUtils.getDailyList();

        // mocking
        given(dailyRepository.findDailiesWithOffsetAndLimit(PageRequest.of(0, 5)))
                .willReturn(dailies);

        given(dailyLikeRepository.selectDailyLikeByDaily(any()))
                .willReturn(MockUtils.getDailyLike());

        given(dailyContentRepository.selectDailyContentByDaily(any()))
                .willReturn(MockUtils.getDailyContent());

        // when
        DailyDTO.ResponseDTOs dtos = dailyService.getDailies(MockUtils.memberName, 0);

        // then
        assertThat(dtos.getResponseDTOS()).isNotNull();
        assertThat(dtos.getResponseDTOS().size()).isEqualTo(3);
        assertThat(dtos.getResponseDTOS().get(0).getThanks1()).isEqualTo("감사1");

    }
}

실제 Service 코드는 여러 Repository를 의존하고 있는데, 이러한 의존성으로부터 Indenpent 해지기 위해서 Mock 객체로 선언하며 의존성을 제거하였다.

이 때 @InjectMocks은 의존성을 주입 받는 객체를 다루고 @Mock은 의존하는 객체를 다룬다.


또한 @SpringBootTest 어노테이션 대신에 @InjectMocks, @Mock, @ExtendWith(MockitoExtensions.class)를 사용함으로써, IoC 컨테이너를 생성하지 않기에 Fast 원칙을 준수할 수 있다.


또한 assertThat 구문을 통해서 사용자가 logSystem.out 모듈등을 통해 직접 확인하지 않고, 테스팅 결과를 확인할 수 있다.


그리고 아까 서론과 본론 초반에서 중점적으로 이야기 했던 Service 계층을 독립적으로 수행 해야 하는 필요성은 복잡한 비즈니스 로직을 구현할 수록 스스로가 더 절실히 느끼는 것 같다.

Service 계층이 단순히 Repository, 영속성 계층으로부터 받은 객체를 DTO등으로 변환 시켜서 Controller에게 전달하는게 아니라고 한다면 스스로가 구현한 비즈니스 로직이 외부의 영향을 받지 않고 정상적으로 원하는대로 동작하는지를 더 갈망하게 될 것이라는 생각이 들었다.

profile
Hello, World! \n

0개의 댓글