Framework/Spring Boot

[Framework] Spring Boot [TDD] 테스트 코드 작성

SeungyubLee 2022. 11. 30. 14:15

TDD(Test Driven Development)란?

테스트 코드를 만들고, 이를 통과하는 최소한의 코드로 시작하여

점진적으로 개선, 확장해가는 개발 방식을 말한다.

 

Article 서비스를 검증하는 테스트 코드를 작성해보자

 

Article 서비스에서 테스트를 원하는 메소드명 우클릭 Generate > Test를 클릭하고

열린 창 하단에 테스트를 할 메소드명을 체크 후 OK를 클릭한다.

 

ArticleServiceTest라는 클래스 파일이 만들어지고

이 파일의 경로는 src > test > java > com.example.firstproject > service가 된다.

 

H2 DB를 사용하고 있었기 때문에 data.sql을 참고해 테스트 코드를 작성한다.

 

예상 시나리오 작성 -> 실제 결과와 비교하여 검증

 

<data.sql>

INSERT INTO ARTICLE(ID, TITLE, CONTENT) VALUES (2, 'AAAA', '1111');
INSERT INTO ARTICLE(ID, TITLE, CONTENT) VALUES (3, 'BBBB', '2222');
INSERT INTO ARTICLE(ID, TITLE, CONTENT) VALUES (4, 'CCCC', '3333');

<ArticleServiceTest>

package com.example.firstproject.service;

import com.example.firstproject.dto.ArticleDto;
import com.example.firstproject.entity.Article;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest // 해당 클래스는 스프링부트와 연동되어 테스팅된다.
class ArticleServiceTest {

    @Autowired ArticleService articleService;

    @Test
    void readAll() {
        // 예상
        Article a = new Article(2L, "AAAA", "1111");
        Article b = new Article(3L, "BBBB", "2222");
        Article c = new Article(4L, "CCCC", "3333");
        List<Article> expectedList = new ArrayList<Article>(Arrays.asList(a, b, c));

        // 실제
        List<Article> articleList = articleService.readAll();

        // 비교
        assertEquals(expectedList.toString(), articleList.toString());
    }

    @Test
    void read_success() { // read 메소드 성공 테스트
        // 예상
        Long id = 2L;
        Article expected = new Article(id, "AAAA", "1111");

        // 실제
        Article article = articleService.read(id);

        // 비교
        assertEquals(expected.toString(), article.toString());
    }

    @Test
    void read_fail() { // read 메소드 실패 테스트 // 존재하지 않는 id를 입력한 경우
        // 예상
        Long id = -1L;
        Article expected = null;

        // 실제
        Article article = articleService.read(id); // return articleRepository.findById(id).orElse(null); 이렇게 작성되어 있으므로 null 값을 갖는 expected로 비교

        // 비교
        assertEquals(expected, article); // null은 toString 메소드를 호출할 수 없음
    }

    @Test
    @Transactional // 조회가 아닌 생성, 변경, 삭제의 경우 Transaction으로 묶어서 Rollback할 수 있게 처리해줘야 한다.
    void create_success() { // create 메소드 성공 테스트 // title과 content만 있는 dto 입력
        // 예상
        String title = "DDDD";
        String content = "4444";
        ArticleDto dto = new ArticleDto(null, title, content);
        Article expected = new Article(1L, title, content);

        // 실제
        Article article = articleService.create(dto);

        // 비교
        assertEquals(expected.toString(), article.toString());
    }

    @Test
    @Transactional // 조회가 아닌 생성, 변경, 삭제의 경우 Transaction으로 묶어서 Rollback할 수 있게 처리해줘야 한다.
    void create_fail() { // create 메소드 실패 테스트 // id가 포함된 dto 입력
        // 예상
        String title = "DDDD";
        String content = "4444";
        ArticleDto dto = new ArticleDto(2L, title, content);
        Article expected = null;

        // 실제
        Article article = articleService.create(dto); // id가 존재하는 경우 null return하도록 작성되어 있음

        // 비교
        assertEquals(expected, article);
    }

    @Test
    @Transactional // 조회가 아닌 생성, 변경, 삭제의 경우 Transaction으로 묶어서 Rollback할 수 있게 처리해줘야 한다.
    void update_success_1() { // update 메소드 성공 테스트 케이스 1 // 존재하는 id와 변경할 title, content만 있는 dto 입력
        // 예상
        Long id = 2L; // 대상 id
        String title = "AAAAAAAA"; // 변경할 title
        String content = "11111111"; // 변경할 content
        ArticleDto dto = new ArticleDto(null, title, content);
        Article expected = new Article(id, "AAAAAAAA", "11111111");

        // 실제
        Article article = articleService.update(id, dto);

        // 비교
        assertEquals(expected.toString(), article.toString());
    }

    @Test
    @Transactional // 조회가 아닌 생성, 변경, 삭제의 경우 Transaction으로 묶어서 Rollback할 수 있게 처리해줘야 한다.
    void update_success_2() { // update 메소드 성공 테스트 케이스 2 작성해보기
    }

    @Test
    @Transactional // 조회가 아닌 생성, 변경, 삭제의 경우 Transaction으로 묶어서 Rollback할 수 있게 처리해줘야 한다.
    void update_fail_1() { // update 메소드 실패 테스트 케이스 1 // 존재하지 않는 id와 변경할 title, content만 있는 dto 입력
        // 예상
        Long id = -1L; // 대상 id
        String title = "AAAAAAAA"; // 변경할 title
        String content = "11111111"; // 변경할 content
        ArticleDto dto = new ArticleDto(null, title, content);
        Article expected = null;

        // 실제
        Article article = articleService.update(id, dto); // id에 해당하는 엔티티가 없는 경우 null return하도록 작성되어 있음

        // 비교
        assertEquals(expected, article);
    }

    @Test
    @Transactional // 조회가 아닌 생성, 변경, 삭제의 경우 Transaction으로 묶어서 Rollback할 수 있게 처리해줘야 한다.
    void update_fail_2() { // update 메소드 실패 테스트 케이스 2 작성해보기
    }

    @Test
    @Transactional // 조회가 아닌 생성, 변경, 삭제의 경우 Transaction으로 묶어서 Rollback할 수 있게 처리해줘야 한다.
    void delete_success_1() { // delete 메소드 성공 테스트 케이스 1 // 존재하는 id 입력
        // 예상
        Long id = 2L;
        Article expected = new Article(id, "AAAA", "1111");

        // 실제
        Article article = articleService.delete(id);

        // 비교
        assertEquals(expected.toString(), article.toString());
    }

    @Test
    @Transactional // 조회가 아닌 생성, 변경, 삭제의 경우 Transaction으로 묶어서 Rollback할 수 있게 처리해줘야 한다.
    void delete_fail_1() { // delete 메소드 실패 테스트 케이스 1 // 존재하지 않는 id 입력
        // 예상
        Long id = -1L;
        Article expected = null;

        // 실제
        Article article = articleService.delete(id);

        // 비교
        assertEquals(expected, article);
    }
}

테스트 코드 작성 후 메소드 옆 재생 버튼 클릭 > Run ArticleServiceTest 클릭하면 해당 메소드의 테스트가 시작된다.

화면 좌측 하단 Show Passed, Show Ignored를 선택하고

메소드명 옆에 녹색 체크(테스트 정상) 또는 X 표시(테스트 실패)를 확인한다.

모든 메소드에 대해 테스트를 진행하고자 한다면 클래스명 옆에 있는 재생 버튼을 클릭하면 된다.

조회가 아닌 생성, 변경, 삭제 테스트의 경우

Transaction으로 묶어서 Rollback할 수 있게 처리해줘야 한다. (어노테이션 추가 @Transactional)