본문 바로가기
Framework/Spring Boot

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

by SeungyubLee 2022. 11. 30.

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)