테스트 주도 개발(TDD)

최지환·2021년 12월 16일
2

TDD(Test Driven Development)

→ 말 그대로 테스트 주도적으로 프로그램을 개발 하는 방법.

전통적으로는 프로그램 개발이 완료된 후에 그 프로그램이 제대로 작동하는지, 테스트를 작성한다. 하지만 테스트 주도개발(TDD)는 프로그램을 개발할 때, 테스트 코드를 먼저 작성하고 그 테스트 코드를 통과하는 실제코드를 나중에 만든다.

예시)
우리가 집을 짓는다고 가정해보자.
집을 짓기 위해 벽돌을 어디에 얼마나 쌓을 건지 특정 영역에 실로 표시를 해두고, 벽돌을 쌓다가 표시해 둔 실 까지 벽돌이 채워지면, 벽돌을 쌓는 것을 중지한다.

TDD를 여기에 비유한다면, 실로 영역을 지정 하는 것을 '테스트 코드', 실제로 벽돌을 쌓는 과정을 '실제 코드'에 비유할 수 있다. 벽돌을 쌓을 때, 벽돌이 튀어나오거나, 모양이 안맞는 것을 실이 '판단'을 해준다.

→ 즉, '실'이라는 테스트 코드가 '벽돌 쌓기'라는 실제 코드가 나아갈 방향을 제시를 해준다.

만약 벽돌을 쌓던 중, 벽돌이 조금 비뚤어졌다면, 다시 반듯하게 잡아야하는데, 이것을 리팩토링에 비유할 수 있다.

💡 리팩토링은 소스코드의 기능은 유지한 채로 소스코드의 디자인을 개선해 나가는 방법이다.

TDD의 흐름

TDD의 일반적인 흐름은 다음과 같다.

  1. 무엇을 테스트 할지 생각한다.
  2. 실패하는 테스트를 작성한다.
  3. 테스트를 통과하는 코드를 작성한다.
  4. 코드를 리팩토링한다. (테스트코드 또한 리팩토링한다.)
  5. 구현해야 할 것이 있을 때 까지 반복.

여기서 N은 " 가장 간단한 코드가 만들어 질 때까지" 이다.


자바 main메소드와 JUnit, AssertJ 설명

1. 자바의 main메소드

→ 프로그램을 시작하고, 구현한 프로그램을 테스트하는 용도로 사용

main메소드로 테스트 하는 경우의 단점

  • 클래스 및 main의 메소드가 커지게 된다면 복잡해지기 때문에, 큰 프로젝트를 테스트 할 시, 버겁다
  • 한 메소드(main)에 여러 테스트 코드가 있어서 어떤 테스트가 어떻게 이루어졌는지 알기가 어렵다.
  • 테스트 결과를 사람이 하나하나 확인해야한다.

TDD의 장점

  • TDD는 코드의 재사용 보장이 가능하다. 따라서 소프트웨어 개발 시, 기능 별 모듈화가 철저하게 이뤄진다. → 필요에 따라 모듈을 추가하거나 제거해도 소프트웨어의 전체 구조에 큰 영향을 끼치지 않는다.
  • 테스트 코드를 먼저 작성하기 때문에 개발자가 구현해야하는 부분에 대한 직관성을 가지기 용이하다. → 테스트 코드를 작성하면서 다양한 예외상황을 구상하기 때문에, 개발 진행 도중 소프트웨어의 전반적인 설계가 변경 될 일 방지.
  • TDD의 경우 버그를 찾기가 쉽다. → 기능 별 테스트 코드를 미리 구상해 뒀기 때문에, 후에 버그를 찾기도 간편하다.

TDD의 단점

  • 생산성의 저하.
    왜냐하면 처음부터 2개의 코드(테스트 코드, 실제코드)를 짜야하고, 중간중간 테스트를 하면서 고쳐나가야 하기 때문

빠른 기간내에 소프트웨어를 작성해야 한다면 실제코드 만 작성 하는것이 효율적이지만,
소프트웨어의 품질, 완성도를 높히기 위해서는 TDD방식이 효율적이다.

JUnit과 AsserJ

JUnit

테스트 코드를 쉽게 사용할 수 있는 자바 라이브러리

AssertJ

다양한 assertion을 제공해주는 자바 라이브러리.

→ 테스트 코드의 가독성을 매우 높혀준다. ( Junit이 기본 제공해 주는 메소드보다 가독성이 매우 높다)


InteliJ에서 TDD 및 그래들 설정 해보기(Java11, IntelliJ)

1

자바는 컴파일 언어이다.

.java 파일을 실행시키는게아니라, .java의 컴파일 결과인 .class을 실행시키는 것이다.

처음 프로젝트 구조를 설헝 할 때는, IntelliJ와 같은 IDE에서는 .class 파일이 담긴 경로를 알아야한다. 이것을 classPath 설정이라고한다

2

gradle을 사용할 때는 classpath가 잘 설정이 되어 있는지 확인해야한다.

→테스트 코드를 실행하기위해 .class의 위치를 알아두기 위함.

우선 프로젝트에 코끼리 모양의 build.gradle 모양 폴터를 누르고,plugin에 'java'가 포함되어 있는지 확인 해야한다.

gradle은 기본 셋팅으로 build 폴더 내부에 컴파일된 소스 구조를 만든다.

3. AssertJ 추가해 보기

우선 그래들을 이용하여 assertJ를 넣어준다.

→ Java 8 버전 이상이면 assertJ 를 3.0 버전 이상으로 셋팅해야한다. 이전 버전은 2.X 버전으로 설정

4. TDD 맛보기

1. 테스트 폴더 설정

project/src/test폴더에서 초록색 java폴더를 클릭해주면, 테스트 폴더를 설정 해줄 수 있다.

다음과 같이 java 폴더를 test 폴더로 설정 해주면 된다.

2. 간단 Calculator 테스트 만들어 보기

java/Calculator.java 생성 후 코드 작성

public class Calculator {

    int num1;
    int num2;

    public Calculator(int num1, int num2) {
        this.num1 = num1;
        this.num2 = num2;
    }

    public int add(int num1, int num2){
        return num1 + num2;

    }
}

소스코드를 작성 하고, command + shift + t 단축어를 누르면 Create Test를 할 수가 있다.

그후에 원하는 버전의 JUnit을 설정하고 test 파일을 만들어 주면 된다.

그렇다면 이렇게 test 폴더안에 CalculatorTest 파일이 생성 된 것을 알 수 있다.

TDD 진짜 시작.

우리는 현재 java/Calculator 에는 생성자 밖에 있지 않다. 즉 계산기의 실질적인 기능들 중 하나인 add, sub, mul 등이 없다.

그중 add 메소드를 TDD 방식으로 만들어 보겠다. 즉 테스트 파일을 먼저 만들고 실제 코드를 작성 해보겠다.

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

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

class CalculatorTest {

    
    @Test
    @DisplayName("add 함수를 위한 테스트 코드 입니다.")
    void add() {
        
        Calculator calculator = new Calculator(1,2);
        
        assertThat(calculator.add(1,2)).isEqualTo(3);

        
    }

}

파일을 작성해보고, 테스트를 해보면 정상으로 실행 되는지 확인이 가능하다.

실행 화면 1 .

1 + 2가 3인지 테스트


사친연산 보충 코드

public class Calculator {

    int num1;
    int num2;

    public Calculator(int num1, int num2) {
        this.num1 = num1;
        this.num2 = num2;
    }

    public int add(int num1, int num2){
        return num1 + num2;

    }

    public int sub(int num1,int num2){
        return num1 - num2;

    }

    public int  div(int num1, int num2) {
        return  num1/num2;

    }

    public int mul(int num1, int num2) {
        return  num1*num2;
    }
}

test 코드

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

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

class CalculatorTest {
    Calculator calculator = new Calculator(1,2);

    @BeforeEach
    void setUp() {
        calculator = new Calculator(1, 2);
    }

    @Test
    @DisplayName("add 함수를 위한 테스트 코드 입니다.")
    void add() {

        assertThat(calculator.add(1,2)).isEqualTo(3);

    }

    @Test
    @DisplayName("sub 함수를 위한 테스트 코드 입니다.")
    void sub() {

        assertThat(calculator.sub(10,5)).isEqualTo(5);
    }

    @Test
    @DisplayName(" div 함수를 위한 테스트 코드 입니다.")
    void div() {

        assertThat(calculator.div(6,2)).isEqualTo(3);
    }

    @Test
    @DisplayName("mul 함수를 위한 테스트 코드 입니다.")
    void mul() {

        assertThat(calculator.mul(2,5)).isEqualTo(10);

    }
}

0개의 댓글

관련 채용 정보