Server/JUnit, Spock

Mockito로 BDD 테스트 코드 작성하기 (BDDMockito)

JaeHoney 2022. 6. 5. 01:00

BDD

BDD란 Danial Terhorst-North와 Charis Matts가 TDD에서 착안한 방법론으로 행위 주도 개발(Behavior-Driven Development)을 말한다.

 

테스트 대상의 상태의 변화를 시나리오를 기반(Narrative)으로 테스트하는 패턴을 주로 사용한다. 이 때 권장하는 행동 패턴은 Given, When, Then 구조이다.

 

해당 패턴은 어떤 상태에서(Given) 어떤 행동을 했을 때(When) 어떤 결과가 되는 지(Then)를 테스트한다.

 

BDDMockito

다양한 테스트 코드를 보면 Mockito를 사용할 때 when().thenReturn()given().thenReturn()패턴이 있는데 전자는 org.mockito.Mockito가 제공하는 기능이고 후자는 org.mockito.BddMockito가 제공하는 기능이다.

 

BDDMockito는 BDD를 사용해서 테스트코드 작성시 시나리오에 맞게 테스트 코드가 읽히도록 개선된 Mockito Framwork이다. BDDMockito는 Mockito를 상속하고 있고 기능도 동일하다. 다만,  BDD 구조로 쉽게 읽힐 수 있도록 도와준다.

 

기존 코드 (Mockito)

아래는 일반 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);
    }
}

Mocking을 하는 것은 행위를 하는 When이 아니라 상태를 설정하는 Given에 해당한다. 그런데 Given 절의 마지막 부분을 보자.

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

Given에 속해야 하는 해당 부분은 객체의 동작을 Mocking하는데 메서드명이 when이다. 이어서 thenReturn도 사용한다. 즉, 개발자가 보기에 테스트 코드에 혼란을 조성한다.

BDDMockito를 사용한 코드

BDDMockito API를 사용하면 위에서 문제로 언급했던 코드를 아래 두 줄로 변경할 수 있다.

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

추가적으로 위에서 Then절의 끝자락에 있는 verify메서드도 then().should_() 메서드로 작성할 수 있다.

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

이후 테스트 메서드를 살펴보자.

@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();
}

해당 테스트의 경우에는 위 코드가 기존의 코드(Mockito)를 사용한 경우보다 훨씬 가독성이 뛰어나다. 이런 공통적인 표준을 정해서 팀에서 사용한다면 더 효율적으로 개발할 수 있을 것 같다. 

 


Reference