본문 바로가기
Test

Mockito가 있는데 왜 BDDMockito를 사용할까?

by wch_t 2024. 6. 10.

1. BDD..? 그게 무엇일까??

BDD, Behavior-Driven Development행위 주도 개발을 뜻한다.

이는 기존의 TDD에서 확장된 개발론으로, "시나리오를 기반으로 한 행위"에 더 많은 관심을 기울인다.

 

 

이 시나리오는 다음과 같은 형식을 따르며, 이를 통해 실제 테스트가 이루어진다.

 

Given(초기 상황) → When(행위, 이벤트) → Then(기대 결과) 

 


 

2. 그럼 BDDMockito와 기존의 Mockito를 사용했을 때 차이점을 살펴보자.

BDD 방식과 Mockito의 기능성을 결합하여, 

BDD의 "Given-When-Then" 구조 스타일의 테스트 작성을 지원하도록 만든 것이 바로 `BDDMockito`이라고 정의를 했다.

 

그럼 기존의 Mockito와 BDDMockito의 코드 차이점을 살펴보자.

(https://jaehoney.tistory.com/220 에 좋은 예제 코드가 있어 가지고 와 소개하였다.)

(해당 코드는 StudyService 클래스의 createNewStudy 메소드를 테스트하는 예제이다.)

 

 

[Mockito 코드]

@ExtendWith(MockitoExtension.class)
public class StudyServiceTest {

    @Mock
    MemberService memberService;

    @Mock
    StudyRepository studyRepository;

    @Test
    void createStudyService() {
        // Given
        StudyService studyService = new StudyService(memberService, studyRepository);

        Member member = new Member();
        member.setId(1L);
        member.setEmail("keesun@gmail.com");

        Study study = new Study(10, "테스트");

        when(memberService.findById(1L)).thenReturn(Optional.of(member));
        when(studyRepository.save(study)).thenReturn(study);
        
        // When
        studyService.createNewStudy(1L, study);
        
        // Then
        assertEquals(member, study.getOwner());
        verify(memberService, times(1)).notify(study);
        verifyNoMoreInteractions(memberService);
    }
}

 

 

코드를 차근차근 살펴보자.

 

 

Given

- StudyService 객체를 생성하는데, 필요한 인자로 모의 memberService, studyRepository를 전달한다.

- 테스트에 사용할 Member, Study 객체를 생성하고 초기화한다.

- memberService의 findById 메소드가 호출될 때 Optional.of(member)를 반환하도록 모의한다.

- StudyRepository의 save 메소드가 호출될 때 study 객체를 반환하도록 모의한다.

*이 때, 문제가 발생한다.
초기 상황을 정의하는 given 에서 when().thenReturn()이 사용이 되고 있다.
하지만 코드를 작성하고 읽는 측면에서 "given에서 when()을 사용하는 게 맞나?" 모순이 발생한 것이다.

이러한 문제를 해결하기 위해 BDDMockito를 사용한다.

 

 

When

- MemberId와 study 객체를 인자로, createNewStudy 메소드를 호출한다.

 

 

Then

- member 객체와 study 객체의 소유자가 같은지 확인한다.

- memberService의 notify 메소드가 한 번 호출되었는지 확인한다.

- memberService에서 notify 메소드 호출 외에, 다른 호출이 전혀 없었는지 확인한다.

 

 


 

 

[BDDMockito 코드]

@Test
void createNewStudy_shouldSameStudy() {
    // Given
    StudyService studyService = new StudyService(memberService, studyRepository);

    Member member = new Member();
    member.setId(1L);
    member.setEmail("keesun@gmail.com");

    Study study = new Study(10, "테스트");

    given(memberService.findById(1L)).willReturn(Optional.of(member));
    given(studyRepository.save(study)).willReturn(study);

    // When
    studyService.createNewStudy(1L, study);

    // Then
    assertEquals(member, study.getOwner());
    then(memberService).should(times(1)).notify(study);
    then(memberService).shouldHaveNoMoreInteractions();
}

 

 

차이점을 위주로 코드를 살펴보자.

 

 

Given

// Mockito
when(memberService.findById(1L)).thenReturn(Optional.of(member));
when(studyRepository.save(study)).thenReturn(study);

// BDDMockito
given(memberService.findById(1L)).willReturn(Optional.of(member));
given(studyRepository.save(study)).willReturn(study);

 

 

Then

// Mockito
verify(memberService, times(1)).notify(study);
verifyNoMoreInteractions(memberService);

// BDDMockito
then(memberService).should(times(1)).notify(study);
then(memberService).shouldHaveNoMoreInteractions();


기능적으로는 앞의 Mockito 코드와 동일하다.

 

하지만 Mockito 스타일에서 BDDMockito 스타일로 변경하면서

테스트 코드가 더 자연스러워져 읽기 쉽고, 테스트의 의도를 더 명확히 드러내는 코드가 되었다.

 

이로써 테스트의 가독성을 높이고, 이해하기 쉬운 테스트 코드 작성이 가능해졌다.

'Test' 카테고리의 다른 글

서비스 로직 Test 작성하기 (feat. Mockito)  (1) 2024.06.10