CS/CS

TDD (Test-Driven Development) feat. JUNIT

annovation 2024. 12. 2. 08:45

TDD

 

TDD(Test-Driven Development 테스트 주도 개발이라는 소프트웨어 개발 방법론입니다. 개발을 시작하기 전에 테스트를 먼저 작성하고, 그 테스트를 통과하도록 최소한의 코드를 작성한 뒤 점진적으로 기능을 구현하는 방식입니다. TDD는 개발과 테스트를 반복하며 안정성과 품질을 높이는 데 중점을 둡니다.

 


프로세스

 

TDD는 Red → Green → Refactor라는 세 단계를 반복합니다

 

 

출처 : https://hanamon.kr/tdd란-테스트-주도-개발/

 

 

 

  1. Red (테스트 실패 상태)
    • 새로운 테스트를 작성하지만, 아직 기능이 구현되지 않아 실패합니다.
    • 이 단계는 코드 작성의 방향을 정합니다.
  2. Green (테스트 통과 상태)
    • 테스트를 통과할 수 있는 최소한의 코드를 작성합니다.
    • 이 단계는 기능이 작동함을 보장합니다.
  3. Refactor (코드 개선)
    • 코드의 중복을 제거하고 가독성을 높이며, 더 나은 설계로 개선합니다.
    • 테스트는 여전히 통과해야 합니다.
    • 이 단계에서는 기능추가를 하면 안됩니다. (TDD 규칙에 어긋나기 때문에)

장점과 단점

 

출처 : https://jimmy7525.tistory.com/m/86

 

* 장점

  1. 코드 품질 향상
    • 테스트를 기반으로 코드를 작성하기 때문에 안정성이 높고 오류가 줄어듭니다.
    • 코드 변경 시 기존 테스트가 동작을 보장해주기 때문에 유지보수가 용이합니다.
  2. 개발 방향 명확화
    • 테스트 작성이 코드 설계의 방향을 정하고, 명확한 목표를 제공합니다.
  3. 디버깅 시간 감소
    • 개발 중 발생하는 오류를 빠르게 탐지하고 수정할 수 있습니다.

 

* 단점

  1. 시간 소모
    • 테스트 작성과 반복 작업으로 초기 개발 속도가 느릴 수 있습니다.
  2. 경험 요구
    • 테스트 작성 경험과 좋은 테스트 사례를 만들기 위한 역량이 필요합니다.
  3. 복잡한 테스트 관리
    • 테스트가 많아질수록 유지보수가 어려워질 수 있습니다.

Java의 TDD JUnit 

JUnit은 Java에서 테스트 주도 개발(TDD)을 지원하는 가장 널리 사용되는 테스트 프레임워크입니다. JUnit을 사용하면 코드의 특정 부분이 올바르게 작동하는지 자동으로 검증할 수 있으며, 반복적인 테스트를 쉽게 수행할 수 있습니다.

 

* 특징

  1. 단위 테스트(Unit Test)
    • JUnit은 메서드 단위로 코드를 테스트합니다.
    • 한 번에 하나의 메서드나 클래스의 동작을 검증합니다.
  2. 테스트 자동화
    • 테스트 코드를 실행하면 예상된 결과와 실제 결과를 자동으로 비교하여 성공/실패를 확인합니다.
  3. 어노테이션 기반
    • 테스트 메서드 작성과 설정이 간단합니다.
    • 주요 어노테이션:
      • @Test: 테스트 메서드를 정의.
      • @BeforeEach / @AfterEach: 테스트 전후 실행할 설정.
      • @BeforeAll / @AfterAll: 테스트 클래스 전체에 대한 설정.
  4. 테스트 리포트
    • 테스트 성공/실패 결과를 명확히 출력하여 디버깅을 돕습니다.
  5. Gradle/Maven 통합
    • 빌드 도구와 쉽게 통합되어 CI/CD 환경에서 사용하기 적합합니다.

 

* 예시

 

1. 테스트 대상 클래스

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public int subtract(int a, int b) {
        return a - b;
    }
}

 

2. Junit 테스트 클래스

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class CalculatorTest {

    @Test
    public void testAdd() {
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        assertEquals(5, result); // 예상 결과와 실제 결과 비교
    }

    @Test
    public void testSubtract() {
        Calculator calculator = new Calculator();
        int result = calculator.subtract(5, 2);
        assertEquals(3, result);
    }
}

 

3. Junit 실행 결과

✓ testAdd() - 성공
✓ testSubtract() - 성공

 

 

* 예제 : 이메일 검증 로직 작성

 

1. RED

  • 테스트 코드 작성 (테스트는 실패함)
  • 아직 구현하지 않은 기능에 대한 테스트를 작성합니다.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class EmailValidatorTest {
    @Test
    void testValidEmail() {
        EmailValidator validator = new EmailValidator();
        boolean isValid = validator.isValid("test@example.com");
        assertTrue(isValid, "이메일 형식이 올바르지 않습니다.");
    }
}

 

➡️ 출력

Error: Cannot resolve method 'isValid'

 

 

2. Green

  • 최소한의 코드를 작성하여 테스트 통과
  • 테스트를 통과하도록 필요한 코드만 작성합니다.
public class EmailValidator {
    public boolean isValid(String email) {
        return true; // 모든 이메일을 유효하다고 가정
    }
}

 

➡️ 출력

Test run finished after 20 ms
[         1 tests successful         ]

 

 

3. Refactor

  • 코드 리팩토링
  • 필요한 경우 가독성, 효율성을 위해 코드를 개선합니다.
import java.util.regex.Pattern;

public class EmailValidator {
    private static final String EMAIL_REGEX = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";

    public boolean isValid(String email) {
        return Pattern.matches(EMAIL_REGEX, email); // 정규식으로 이메일 형식 검증
    }
}

 

➡️ 출력

Test run finished after 25 ms
[         2 tests successful         ]

프로젝트 구조

 

* 서로 다른 package에 있는 main에 있는 class를 test package에서 import 없이 사용할 수 있는 이유?

src/
├── main/
│   ├── java/
│   │   └── com/
│   │       └── example/
│   │           └── Calculator.java
├── test/
    ├── java/
        └── com/
            └── example/
                └── CalculatorTest.java

 

Java 프로젝트는 일반적으로 두 개의 디렉토리(폴더와 같은 개념) 구조를 사용합니다:

  • src/main/java : 애플리케이션의 실제 코드(Production Code)
  • src/test/java : 테스트 코드

빌드 도구(예: Maven, Gradle)는 이 구조를 인식하고, src/main/java에 있는 클래스들을 자동으로 테스트 클래스 경로(Classpath)에 포함합니다.

클래스가 자동으로 참조되므로 test package에서는 main package에 있는 class를 import 하지 않아도 됩니다.


TDD 요약

  1. 테스트를 먼저 작성하고, 이를 통해 요구사항을 명확히 정의
  2. 실패하는 테스트에서 시작하여, 테스트를 통과하는 코드를 점진적으로 작성
  3. 리팩터링을 통해 코드 품질과 유지보수성을 높임
  4. 인생은 TDD 처럼

참고하면 좋은 사이트

 

1. 유튜브 참고 영상 : 우아한 테크

 


출처

OpenAI ChatGPT (https://openai.com)

 

TDD 이미지 출처 : https://hanamon.kr/tdd란-테스트-주도-개발/

 

TDD란? 테스트 주도 개발 - 하나몬

TDD란 Test Driven Development의 약자로 '테스트 주도 개발'이라고 한다.

hanamon.kr

TDD 프로세스 이미지 출처 : https://jimmy7525.tistory.com/m/86

 

TDD - UI Test

테스트 주도 개발 TDD (Test-Driven Development) 은 매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 프로세스 중 하나이다. 개발자는 먼저 요구 사항을 검증하는 자동화 된 테스트 케이스를 작성한

jimmy7525.tistory.com

 

반응형