본문 바로가기
Tech/Test

Mockito vs BDDMockito

반응형

Mockito

  • Mockito란?
    • 복잡하게 얽혀있는 객체들의 의존성 때문에 원하는 비즈니스 로직에 대한 테스트를 작성하기 힘들다
    • 이때 의존성을 가지는 객체들을 가짜 객체를 만들어 테스트할 수 있는데 이런 객체를 Mock 객체라고 한다
    • Mockito는 이름에서도 알 수 있다시피 관리가 어려운 Mock 객체를 손쉽게 사용하도록 지원해주는 프레임워크이다.
  • 생성 방식
    • mock method
      // JUnit 5
      import static org.mockito.Mockito.mock;
      
      public class MemberServiceTest2 {
      
          private MemberRepository memberRepository;
          private MemberService memberService;
      
          @BeforeEach
          void setUpTest() {
              memberRepository = mock(MemberRepository.class);
              memberService = new MemberService(memberRepository);
          }
      
          ...
      
      }
      • org.mockito.Mockito.mock를 통해 해당 클래스에 맞는 Mock 객체를 생성할 수 있다
    • @Mock 어노테이션
      import org.junit.jupiter.api.extension.ExtendWith;
      import org.mockito.InjectMocks;
      import org.mockito.Mock;
      
      @ExtendWith(MockitoExtension.class)
      class MemberServiceTest {
      
          @Mock
          MemberRepository memberRepository;
      
          @InjectMocks
          MemberService memberService;
      
          ...
      
      }
      @Mock 어노테이션을 이용해 Mock 객체를 생성하고, @InjectMocks를 이용해 해당 클래스로 의존성 주입을 할 수 있다
  • 사용 방법
    • 생성된 Mock 객체의 사용 방법은 간단하다
    • when 메서드를 통해 원하는 동작을 미리 정하고 이를 기반으로 테스트할 수 있다.
    • 또한 호출 등이 정확히 됐는지 확인할 수 있는 verify 메서드를 지원한다
      // JUnit5 + AssertJ
      
      // given
      Mockito.when(memberRepository.findAll())
             .thenReturn(List.of(Member.builder().name("블로그주인1").age(30).address("구로구").build(),
                                 Member.builder().name("블로그주인2").age(31).address("광진구").build()));
      
      // when
      List<MemberResponse> actual = memberService.getMembers();
      
      // then
      assertThat(actual).isNotEmpty();
      assertThat(actual).extracting("name","age","address")
                        .contains(tuple("블로그주인1",30,"구로구")
                                  , tuple("블로그주인2",31,"광진구"));
      
      Mockito.verify(memberRepository).findAll();
      • verify methd는 두번째 인자로 VerificationMode를 갖게되는데 호출횟수(times, never, atleast)를 지정할 수 있다

 

BDDMockito

  • Behavior-Driven Development Mockito 로 풀어쓸 수 있다
  • BDD는 즉 행위 주도 개발을 말한다. 
  • 테스트 대상의 상태의 변화를 테스트하는 것이고, 시나리오를 기반으로 테스트하는 패턴을 권장한다.
  • BDD 권장 행동 패턴은 Given, When, Then 로 나눠진다
  • 음 위의 내용은 이해했는데 그럼 Mockito랑 무슨 차이지?
    • 위에서 소개했던 Mockito의 사용 방법 예시를 보면 뭔가 어색한(?) 곳을 찾을 수 있다
      // Mocito
      
      @ExtendWith(MockitoExtension.class)
      class MemberServiceTest {
      
          @Mock
          MemberRepository memberRepository;
      
          @InjectMocks
          MemberService memberService;
      
      	@Test
          void test() {
          	// given
              Mockito.when(memberRepository.method)....
              
              // when
              
              // then
              Mockito.verify(memberRepository)....
          }
      
      }


    • 혹시.. 어색한 곳을 찾으셨을까요?
    • 바로 given 조건에서 Mockito.when 메서드를 이용해 Stubbing을 하는 곳이다
    • BDD 시나리오에 맞게 given 조건에서 명칭이 같은 ( 가독성에 혼돈을 주지않는 ) 메서드를 사용하기 위해 BDDMockito가 등장했다
  • BDDMockito 적용
    // dependency
    
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>2.21.0</version>
    </dependency>

    BDDMockito도 Mockito와 마찬가지로 mockito-core 안에 있다


    // JUnit5 + AssertJ + BDDMockito
    
    
    @ExtendWith(MockitoExtension.class)
    public class MemberServiceBddTest {
    
        @Mock
        MemberRepository memberRepository;
    
        @InjectMocks
        MemberService memberService;
    
    
        @DisplayName("멤버 전체 조회")
        @Test
        void getMembersTest() {
            // given
            BDDMockito.given(memberRepository.findAll())
                      .willReturn(List.of(Member.builder().name("test1").age(30).address("구로구").build(),
                                          Member.builder().name("test2").age(31).address("광진구").build()));
    
            // when
            List<MemberResponse> actual = memberService.getMembers();
    
            // then
            assertThat(actual).isNotEmpty();
            assertThat(actual).extracting("name","age","address")
                              .contains(tuple("test1",30,"구로구"),
                                        tuple("test2",31,"광진구"));
    
            BDDMockito.then(memberRepository).should(BDDMockito.times(1)).findAll();
        }
    }
    • Mockito.when -> BDDMockito.given
    • Mockito.verify -> BDDMockito.then
    • BDDMockito는 Mockito 클래스를 상속하여 추가 Wrapping한 클래스라고 봐도 무방할 것 같다
      ( 즉 이름만 다를뿐 사용법은 같다 )
      // BDDMockito class
      
      public class BDDMockito extends Mockito {
      	....
      }

 

 

참고 자료

 

Mockito와 BDDMockito는 뭐가 다를까?

이 글은 우아한테크코스 리뷰 페이지에 함께 게시된 글입니다. 해당 게시글은 JUnit5.x를 기준으로 작성되었습니다. 우아한테크코스 레벨2 미션 중에 의문이 생긴 적이 있었다.

velog.io

 

Quick Guide to BDDMockito | Baeldung

A quick guide to using BDDMockito's fluent API.

www.baeldung.com

 

728x90
반응형

'Tech > Test' 카테고리의 다른 글

Embedded MySQL를 이용해 테스트 환경 구축해보기  (0) 2021.09.29
JsonPath란?  (0) 2021.09.06
JUnit 5 공식 레퍼런스  (0) 2021.08.25
Jacoco를 이용해 테스트 커버리지 관리해보자  (0) 2021.08.08