TIL

day63 TIL

dalooong 2023. 7. 20. 15:45

TDD란?

Test Driven Devleopmen의 약자로, 일반적인 개발의 흐름인 코드를 작성한 뒤, 테스트를 한다가 아닌, (실패할) 테스트 코드를 먼저 잓어하고, 해당 테스트를 통과하는 최소한의 코드를 작성 한 뒤 리펙토링을 진행하는 형태의 개발 방법론입니다.

유닛 테스트 코드를 먼저 작성하고 연습을 통해 요구사항에 대한 더 상세한 이해를 추가하는 개발 방법론입니다.

 

TDD 특징 

일반적인 개발의 흐름

1. 요구사항에 따른 설계

2. 코드 개발

3. 테스트 

 

TDD의 개발의 흐름

1. 요구사항 기반 테스트 작성

2. 테스트를 통과하는 코드 작성

3. 통과한 코드의 품질 향상

4. (요구사항에 대한 향상된 이해를 바탕으로) 테스트를 기반으로 설계 수정

RED

아직 작성되지 않은 기능에 대한 테스트 코드 작성

작성하지 않았기 때문에 실패하게 된다.

 

GREEN

테스트를 통과하는 최소한의 코드 작성

 

REFACTOR

필요에 따라 코드의 품질 개선 

 

간단한 계산기 TDD 실습

실습준비 

 

CalculatorTests 테스트 클래스 생성 

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class CalculatorTests {
    // 1. 테스트를 작성한다.
    @Test
    // 덧셈 테스트
    public void additionTest(){
        // 2. 테스트를 실행하고 실패한다.
        Calculator calculator = new Calculator();

        assertEquals(5, calculator.add(2,3));
        assertNotEquals(5, calculator.add(3, 3));
    }
    // 뺄셈 테스트
    @Test
    public void subtractionTest(){
        Calculator calculator = new Calculator();

        assertEquals(3, calculator.sub(5,2));
    }

    // 곱셈 테스트
    @Test
    public void multipleTest(){
        Calculator calculator = new Calculator();
        assertEquals(9, calculator.multiple(3, 3));
    }

    // 나눗셈 테스트
    @Test
    public void divisionTest(){
        Calculator calculator = new Calculator();
        assertEquals(4, calculator.division(8, 2));
    }

    // 나눗셈 테스트2
    // 기본은 반환값이 정수
    // 만약 0으로 나누려고 한다면
    // IllegalArgumentException 발생
    @Test
    public void divisionExceptionTest() throws IllegalAccessException {
        Calculator calculator = new Calculator();
        assertEquals(3, calculator.divide(6,2));
       Throwable exception =  assertThrows(IllegalAccessException.class, () -> calculator.divide(6,0));
    }

    // 3. 테스트를 통과하는 코드를 작성한다.
    private class Calculator {
        //덧셈
        public int add(int a, int b){
            return a + b;
        }

        //뺄셈
        public int sub(int a, int b) {
            return a - b;
        }

        //곱셈
        public int multiple(int a, int b) {
            return a * b;
        }

        //나눗셈
        public int division(int a, int b) {
            return a / b;
        }

        //나눗셈예외
        public int divide(int a, int b) throws IllegalAccessException {
            if (b == 0)
                throw new IllegalAccessException("divide by Zero");
            return a / b;
        }
    }
}

 

테스트 코드 작성 순서

1. 테스트를 작성한다.

2. 테스트를 실행하고 실패한다.

3. 테스트를 통과하는 코드를 작성한다. 

 

게시글 테스트 만들기

 

1. articleRepository를 생성하여 데이터베이스  소통의 창구를 마련 

ArticleRepositoryTest 클래스 생성

 

@DataJpaTest
public class ArticleRepositoryTest {
    @Autowired
    private ArticleRepository articleRepository;
    
    @Test
    // 1. articleRepository를 생성하여 데이터베이스 소통의 창구를 마련했다.
    public void articleRepositoryIsNotNull(){
        assertThat(articleRepository).isNotNull();
    }
}

ArticleRepository 인터페이스 생성

public interface ArticleRepository extends JpaRepository<Article, Long> {
}

 

 Article 엔티티 생성 

 

@Entity
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}

 

 

2. createArticle 테스트 생성 

 

package com.example.tdd;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;


@DataJpaTest
public class ArticleRepositoryTest {
    @Autowired
    private ArticleRepository articleRepository;
    
    @Test
    // 1. articleRepository를 생성하여 데이터베이스 소통의 창구를 마련했다.
    public void articleRepositoryIsNotNull(){
        assertThat(articleRepository).isNotNull();
    }

    @Test
    public void createArticle(){
        // given
        Article article = new Article();
        article.setTitle("Hello!");
        article.setContent("TDD is hard");

        // when
        article = articleRepository.save(article);

        // then
        assertThat(article).isNotNull();
        assertThat(article.getId()).isNotNull();
        assertEquals("Hello!", article.getTitle());
        assertEquals("TDD is hard", article.getContent());
    }

    @Test
    public void findByTitleContains(){
        // given
        Article article = new Article();
        article.setTitle("hello");
        articleRepository.save(article);

        article = new Article();
        article.setTitle("Yellow");
        articleRepository.save(article);

        // when
        List<Article> result1 = articleRepository.findAllByTitleContains("ell");
        List<Article> result2 = articleRepository.findAllByTitleContains("he");
        List<Article> result3 = articleRepository.findAllByTitleContains("A");

        // then
        assertEquals(2, result1.size());
        assertEquals(1, result2.size());
        assertEquals(0, result3.size());
    }
}
@Entity
public class Article {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Getter
    private Long id;

    @Getter
    @Setter
    private String title;

    @Getter
    @Setter
    private String content;
}
public interface ArticleRepository extends JpaRepository<Article, Long> {
    List<Article> findAllByTitleContains(String title);

}

 

Service 개발 

 

 

Controller 개발