Java Spring boot에 BDD 적용 with Cucumber

Cori1304·2025년 12월 4일

BDD

TDD에서 파생됨, TDD는 리소스가 많이 들어서 개발 시간 지연이라는 문제를 발생 시킴 그렇기에 BDD로 행동에 대한 중점 테스트를 하기 시작함, 하지만 TDD와 BDD는 상호보완 관계 테스트하는 영역이 다르기에 더 안전한 서비스를 만들 수 있음

BDD 사용 순서

1.명세 작성
PO, 기획자, 개발자 등이 모여 Gherkin 문법으로 .feature 파일을 작성하고 모두 동의를 해야한다.(이 시점에서 아직 구현 코드도 작성 안 함)
-> Ex) Given 계산기에 2와 3을 입력하고...

2.Step Definition 자동화
개발자는 합의된 .feature 파일을 가져와 Step Definition (Java 테스트 코드)을 작성한다. (이때, 테스트를 실행하면 Step Definition 코드는 실제 서비스 로직을 찾지 못해 실패(Red) 상태가 되기에 TDD의 Red 단계와 유사)

3. 기능 구현

개발자는 이 실패한 Step Definition(테스트 코드)을 통과시키기 위해 최소한의 서비스 로직(생산 코드)을 작성합니다.

테스트 코드를 나중에 작성해도 될까?

결론은 그러면 안 된다. BDD는 통합 테스트로 이루어질 확률이 높다. 그렇기에 초기에 구상한 테스트 코드에 의존성이 추가되거나 변경되는 것은 당연하다. BDD의 테스트 코드 비즈니스 로직이 아닌 기술적인 구현 세부사항에 의존해야 하기에 변경은 상관없다. 테스트 코드 변경을 감수하고 비즈니스 코드를 작성하면 된다.


Cucumber

  • spring boot에서 사용할 프레임 워크, .feature 파일 작성 하여
  • jira 티켓에 [given, when, then]을 작성 -> 개발자와 비개발자 모두 확인할 수 있도록 (Gherkin 문법으로 작성 필수)

프로젝트 적용 예시

주어진 .feature 시나리오를 바탕으로 Java Step Definition 코드를 작성하겠다.
이 과정은 Gherkin 문장실제 Java 클래스의 메소드에 연결(Glue)하고, 그 메소드 안에서 애플리케이션의 핵심 로직을 호출하는 방식으로 진된다.

.feature

Feature: 계산기 기능

  Scenario: 두 수를 더한다
    Given 계산기에 2와 3을 입력하고
    When 더하기를 수행하면
    Then 결과는 5가 되어야 한다

.feature를 위한 Java Step Definition

1단계: 프로젝트 환경 준비 (Calculator.java)

테스트할 핵심 로직을 가진 간단한 Calculator 클래스를 준비합니다. 이 클래스는 Spring Bean으로 간주하고 테스트 환경에서 주입받아 사용합니다.

// src/main/java/com/example/app/Calculator.java

package com.example.app;

// 실제 애플리케이션의 핵심 로직
public class Calculator {
    private int result;

    public void input(int a, int b) {
        // 실제 입력 처리 로직 (필요하다면)
    }

    public void add(int a, int b) {
        this.result = a + b;
    }

    public int getResult() {
        return this.result;
    }
}

2단계: Step Definition 클래스 작성 (CalculatorSteps.java)

.feature 파일의 Gherkin 문장들을 처리할 Java 클래스를 작성한다. 보통 src/test/java/com/example/steps와 같은 패키지에 위치하며, Spring 환경을 사용하기 위한 설정이 포함된다.

// src/test/java/com/example/steps/CalculatorSteps.java

package com.example.steps;

import com.example.app.Calculator;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.jupiter.api.Assertions.assertEquals;

// 1. SpringBootTest 어노테이션을 사용하여 Spring 컨텍스트를 로드합니다.
//    (실제 Spring Boot 환경에서는 별도의 @ContextConfiguration 설정이 필요할 수 있습니다.)

public class CalculatorSteps {

    // 2. 테스트 대상인 Calculator Bean을 주입받기 위한 필드
    private Calculator calculator = new Calculator(); // 간단 예시를 위해 직접 인스턴스화

    // 테스트에 사용할 임시 변수
    private int number1;
    private int number2;
    private int actualResult;

    // =========================================================================
    // 3. GIVEN 절 구현: Given 계산기에 2와 3을 입력하고
    // =========================================================================
    // 정규 표현식: ^...$는 문장의 시작과 끝을 의미합니다. (\\d+)는 숫자를 캡처합니다.
    @Given("계산기에 {int}와 {int}을 입력하고")
    public void 계산기에_두_수를_입력하고(int a, int b) {
        // Cucumber가 feature 파일에서 '2'와 '3'을 추출하여 a와 b 인수로 전달합니다.
        this.number1 = a;
        this.number2 = b;
        // 입력 처리가 있다면 여기서 로직을 호출합니다.
    }

    // =========================================================================
    // 4. WHEN 절 구현: When 더하기를 수행하면
    // =========================================================================
    @When("더하기를 수행하면")
    public void 더하기를_수행하면() {
        // 실제 애플리케이션의 핵심 로직을 호출합니다.
        calculator.add(this.number1, this.number2);
        this.actualResult = calculator.getResult(); // 결과를 저장합니다.
    }

    // =========================================================================
    // 5. THEN 절 구현: Then 결과는 5가 되어야 한다
    // =========================================================================
    @Then("결과는 {int}가 되어야 한다")
    public void 결과는_기대값과_같아야_한다(int expectedResult) {
        // Cucumber가 feature 파일에서 '5'를 추출하여 expectedResult 인수로 전달합니다.

        // JUnit의 Assertion을 사용하여 실제 결과와 기대 결과를 비교하여 검증합니다.
        assertEquals(expectedResult, this.actualResult, "두 수의 덧셈 결과가 기대값과 다릅니다.");
    }
}

3단계: 테스트 러너 실행

CucumberTestRunner 실행시 Cucumber 동작 순서

  1. .feature 파일의 Given 계산기에 2와 3을 입력하고 문장을 읽습니다.
  2. CalculatorSteps.java@Given("계산기에 {int}와 {int}을 입력하고") 메소드를 찾습니다.
  3. 23을 추출하여 number1number2 변수에 저장합니다.
  4. When 더하기를 수행하면 문장을 읽고, @When("더하기를 수행하면") 메소드를 실행합니다.
  5. calculator.add(2, 3)을 호출하고, 결과 5actualResult에 저장합니다.
  6. Then 결과는 5가 되어야 한다 문장을 읽고, @Then("결과는 {int}가 되어야 한다") 메소드를 실행합니다.
  7. assertEquals(5, actualResult)를 실행하여 테스트를 성공으로 처리합니다.
profile
개발 공부 기록

0개의 댓글