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 개발