티스토리 뷰

안녕하세요 강정호 입니다.

 

이번에는 게시글 서비스를 테스트하는 로직을 구현해보려고 합니다.

 

ArticleServiceTest 코드

package com.marathon.board.service;

import java.util.List;

import com.marathon.board.domain.type.SearchType;
import com.marathon.board.dto.ArticleDto;
import com.marathon.board.repository.ArticleRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;

@DisplayName("게시글 테스트")
@SpringBootTest
@ExtendWith(MockitoExtension.class)
public class ArticleServiceTest {

    @InjectMocks private ArticleService sut;
    @Mock private ArticleRepository articleRepository;

    @DisplayName("게시글을 검색하면, 게시글 리스트를 반환한다.")
    @Test
    void givenSearchParameters_whenSearchingArticles_thenReturnArticleLists(){
        // Given
        //SearchParameters searchParameters = SearchParameters.of();

        // When
        Page<ArticleDto> articles = sut.searchArticles(SearchType.TITLE, "search keyword");

        // Then
        Assertions.assertThat(articles).isNotNull();
    }

    @DisplayName("게시글을 조회하면, 게시글을 반환한다.")
    @Test
    void givenArticleId_whenSearchingArticle_thenReturnArticle(){
        // Given
        //SearchParameters searchParameters = SearchParameters.of();

        // When
        ArticleDto articles = sut.searchArticle(1L);

        // Then
        Assertions.assertThat(articles).isNotNull();
    }

}

@SpringBootTest 

단위테스트와 같은 검증이 아닌 스프링에서 실제 운영환경과 같이 전체 플로우가 잘 동작하는지 확인하기 위한

통합테스트이다. SpringBootTest는 실행시 SpringBootApplication을 사용하여 모든 빈을 등록한다.

그렇다보니 테스트 소요시간이 길고 메모리를 많이 사용하게 된다.

 

@InjectMock과 @Mock의 차이

@Mock 어노테이션은 목 객체를 생성하는 데 사용됩니다. 이 어노테이션을 사용하면 해당 필드에 대한 목 객체가 자동으로 생성됩니다. @Mock 어노테이션이 붙은 필드는 테스트할 객체에서 의존성으로 사용되는 객체를 목 객체로 대체할 수 있습니다. 이렇게 하면 해당 필드에 대한 목 객체를 직접 생성하고 관리할 필요가 없습니다.

 

@Mock으로 ArticleRepository를 생성한다.

@InjectMocks 어노테이션은 ArticleService 클래스의 인스턴스를 생성하면서 @Mock 어노테이션으로 생성된 목 객체를 해당 필드에 주입(inject)합니다. 이렇게 함으로써 ArticleService 객체는 목 객체가 주입된 상태로 생성됩니다.

 

위의 2가지 테스트는 다음을 테스트합니다.

1) 제목으로 게시글을 검색하면 게시글 리스트를 반환하는지 여부

2) 특정 게시글을 게시글 아이디로 조회하면, 해당 게시글을 반환하는지 여부

 

위 테스트를 진행하기 위한 ArticleService 코드는 다음과 같습니다.

package com.marathon.board.service;

import java.time.LocalDateTime;
import java.util.List;

import com.marathon.board.domain.type.SearchType;
import com.marathon.board.dto.ArticleDto;
import com.marathon.board.repository.ArticleRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Transactional
@Service
public class ArticleService {

    private final ArticleRepository articleRepository;


    @Transactional(readOnly = true)
    public Page<ArticleDto> searchArticles(SearchType title, String searchKeyword) {
        // Page 안에 이미 페이지네이션 구현이 되어있다.
        return Page.empty();
    }

    @Transactional(readOnly = true)
    public ArticleDto searchArticle(long l) {

        return null;
    }
}

@Transactional 어노테이션

DB의 데이터무결성, 동시성 제어, 예외처리 등을 보장하고 여러개의 논리적 연산을 하나의 묶음으로 처리하기 위해서 @Transactional 어노테이션을 사용한다. Article을 생성, 갱신, 삭제 등을 할 때 1개의 트랜잭션으로 묶어주어 데이터의 일관성 유지.

 

  메서드에 @Transactional(readOnly = true)를 사용하면 좋은 점

  1. 성능 향상: readOnly 속성을 true로 설정하면 트랜잭션 동안 변경 작업을 수행하지 않음을 알리므로, 데이터베이스의 락을 획득하지 않고 읽기 작업을 수행할 수 있습니다. 이는 성능 향상을 가져올 수 있습니다. 변경 작업이 필요하지 않은 읽기 전용 트랜잭션에서는 데이터베이스의 일관성을 유지하기 위해 록을 사용할 필요가 없습니다.
  1. 동시성 제어: 읽기 전용 트랜잭션은 동시에 여러 개의 트랜잭션에서 접근할 수 있습니다. 따라서 동시성 문제를 줄이고, 다중 스레드 환경에서 안전하게 작업할 수 있습니다. 반면에 읽기와 쓰기 작업을 함께 하는 트랜잭션에서는 록 충돌이 발생할 수 있으며, 이는 성능 저하와 동시성 문제를 야기할 수 있습니다.
  2. 데이터 무결성 보장: 읽기 전용 트랜잭션에서는 데이터를 수정하지 않기 때문에 데이터 무결성이 보장됩니다. 읽기 작업만 필요한 메서드에서는 readOnly 속성을 사용하여 실수로 데이터를 수정하는 문제를 방지할 수 있습니다.
  3. 코드 의도 명확화: @Transactional(readOnly = true)를 사용함으로써 해당 메서드가 읽기 작업에만 사용되는 것임을 명확하게 표현할 수 있습니다. 다른 개발자들이 코드를 이해하고 유지보수하는 데 도움이 됩니다.

 

 

댓글