BDD(Behavior-Driven Development)

소프트웨어 마에스트로 14기 팀 프로젝트에서 나는 백엔드 파트를 담당하여 Spring Boot를 통한 WAS 개발을 하게 되었다. 전담 멘토님께서 백엔드에서 지금 당장 TDD를 도입하기 보다는 BDD를 도입하는 것을 권장하셨고, BDD에 대해 이해해보고자 게시글을 작성하게 되었다.


BDD?

  BDD(Behavior-Driven Development)는 소프트웨어 개발 방법론 중 하나로, 행동 주도 개발을 의미한다. BDD는 사용자 관점에서 시스템의 행동을 설명하는데 집중하며, 이를 통해 개발자와 비개발자 모두가 이해하기 쉬운 언어로 소프트웨어의 요구사항을 명확하게 정의하는 것을 목표로 한다.

TDD와의 차이

  BDD는 TDD(Test-Driven Development, 테스트 주도 개발)의 확잠 개념으로 볼 수 있다. TDD는 코드 레벨에서의 테스트를 중심으로 개발을 진행하지만, BDD는 사용자의 행동에 집중하여 시스템 전체의 동작을 이해하고, 이를 바탕으로 개발을 진행하는 것을 의미한다.

특징

  BDD는 ‘시나리오’라는 개념을 사용하여 사용자의 행동과 예상 결과를 설명한다. 대표적인 시나리오 작성 형식은 “Given-When-Then” 형식이다.

  • Given: 시나리오가 시작되는 초기 조건 (ex: 사용자가 로그인 페이지에 있다)
  • When: 사용자의 행동을 설명 (ex: 사용자가 로그인 버튼을 누른다)
  • Then: 행동에 따른 시스템의 반응이나 결과를 설명 (ex: 사용자는 메인 페이지로 이동한다)

  위와 같이 사용자 중심의 시나리오를 통해 개발자와 비개발자 모두가 이해할 수 있는 요구사항 명세를 작성하고, 이를 바탕으로 소프트웨어를 개발하는 것이 BDD의 주요 목표이다.

Spring에서 BDD 적용 예시

import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    private User user;

    @BeforeEach
    public void setUp() {
        user = new User();
        user.setId(1L);
        user.setName("John Doe");
        user.setEmail("johndoe@example.com");
    }

    @Test
    public void shouldFindUserById() {
        // Given
        given(userRepository.findById(1L)).willReturn(Optional.of(user));

        // When
        User foundUser = userService.getUserById(1L);

        // Then
        assertThat(foundUser).isEqualTo(user);
    }
}

  Spring에서는 BDDMockito라는 라이브러리를 통해 BDD 스타일의 테스팅을 진행할 수 있다. BDDMockito는 Mockito의 BDD 스타일 API를 제공한다.
  주요 사용 방법은 given 메서드를 통해 예상되는 동작을 설정하고, then 메서드를 통해 그 결과를 검증하는 것이다. 아래 설명을 통해 코드를 살펴보자.

  • given(userRepository.findById(1L)).willReturn(Optional.of(user));: 주어진 상황(given)을 설정하는 부분, 여기서는 1L로 User를 조회하면 user 인스턴스를 반환하라는 의미
  • User foundUser = userService.getUserById(1L);: 사용자의 행동(when)을 설명하는 부분, 여기서는 사용자가 실제로 1L을 통해 User를 조회함
  • assertThat(foundUser).isEqualTo(user);: 행동에 따른 결과(then)를 확인하는 부분, 여기서는 개발자가 예상하고 있는 결과(user 인스턴스)와 실제 결과(foundUser)가 동일한지 assertThat 메서드를 통해 검증함