본문 바로가기
개발/Test

[Test] Mockito를 사용해보자 (2)

by solchan98 2022. 2. 10.

이전 포스트에서 Mockito가 무엇이고 어떻게 사용하는지 기본적으로 알아보았다.
이번에는 Mockito에서 제공하는 BDD스타일을 정리한다.

Mockito의 BDD스타일

이전 포스트에서 Mock객체를 만들어 Stubbing하는 방법을 알아보았다.
Mockito에서는 BDD라는 스타일의 방식으로 Stubbing을 제공한다.

BDD가 뭔데?

BDD는 'Behavior Driven Develop'의 약자로 클래스의 행위에 대한 테스트를 진행한다는 의미이다.
테스트 케이스의 메서드명을 '~클래스는 ~행위를 해야한다'라는 식의 문장으로 작성하며 어떠한 시나리오를 기반하여 작성하는 개발 방법이라고 한다.
(BDD에 대해 좀 더 공부하여 개별 포스팅을 하고 링크를 추가해야겠다.)
기본적인 Mock객체를 Stubbing하는 방식을 Mockito에서는 BDD방식으로 사용할 수 도록 지원한다.

Mockito의 BDD

Mockito에서 BDD의 개념을 적용해보면 다음과 같다.
우선, BDD는 시나리오를 기반하여 테스트 케이스를 작성한다.
작성된 시나리오에 따라 상황이 부여되고 결과가 기대값과 같은지 비교를 한다.
이 단계를 크게 Given - When - Then으로 나누어 진행한다.

  • Given
    시나리오에서 필요한 데이터를 세팅하는 단계이다.
    만약 담배를 사는 시나리오의 경우, 담배를 구매하는 사람과 가게, 가게의 판매 메서드가 필요하다. 이렇게 필요한 데이터를 부여하는 단계이다.
    mocking하는 과정 또한 given단계에 포함된다.
  • When
    이제 부여된 상황이 일어나는 단계이다. 즉, 이 단계가 행동이라고 볼 수 있다.
    만약, 미성년자가 담배를 구매하는 시나리오라고 한다면 담배를 구매하는 것이 바로 when의 단계인 것이다. 즉 담배 구매라는 행동을 실제 수행하는 단계이다.
  • Then
    행동을 수행하고, 수행 결과와 기대값을 비교하는 단계이다.
    미성년자가 담배를 구매하려고 한다면 판매가 수행되어서는 안된다.
    즉, then에서는 판매를 하지 않았음을 확인하는 단계이다.

위 단계를 문장으로 정리하면 다음과 같다.
담배를 판매하는 가게가 있고, 미성년자 손님이 있다.
미성년자 손님이 담배를 구매하려고한다. (담배 판매 로직에서 reject)
가게는 미성년자 손님에게 담배를 판매하면 안된다.(이전 단계의 결과와 비교)
비교 결과 같으면 테스트는 성공한 것이다.
위 BDD 설명에서 말했던 '~클래스는 ~행위를 해야한다'에 대입해보면
'가게(클래스는) 미성년자에게 담배를 판매하면 안된다'로 테스트 케이스명을 작성할 수 있다.

BDD스타일로 적용해보기

기본 방식과 BDD로 변경하면서 차이를 확인해보자
시나리오는 다음과 같이 진행한다.

미성년자 손님과 담배를 판매하는 가게가 있다.
미성년자는 담배를 구매하려고 한다,
가게는 미성년자에게 담배를 판매해서는 안된다.

코드 작성

도메인은 Customer와 Store를 생성하고, CustomerSerive를 인터페이스로 작성하며 StoreService를 작성한다.
customerSerivce 인터페이스를 mocking하여 미성년자 손님을 응답하도록한다.

// 도메인
/// Customer
public class Customer {
    private String name;
    private Long age;

    public Customer(String name, Long age) {
        this.name = name;
        this.age = age;
    }

    public Long getAge() {
        return this.age;
    }
}

/// Store
public class Store {
    private String name;

    public Store(String name) {
        this.name = name;
    }
}
// StoreService
public class StoreService {

    private final CustomerService customerService;
    public StoreService(CustomerService customerService) {
        this.customerService = customerService;
    }

    public String sellCigar(String name) {
        Customer customer = customerService.findByName(name);
        if(customer.getAge() < 20) {
            return "미성년자에게는 판매할 수 없습니다.";
        }
        return "Cigar";
    }
}

// CustomerService (Interface)
public interface CustomerService {
    Customer findByName(String name);
}

기본 방식

기본 방식으로 위 시나리오의 테스트 케이스를 작성하면 다음과 같다.

    @Mock
    private CustomerService customerService;

    @Test
    @DisplayName("미성년자 담배 구매 - 기본 방식")
    void testBasic() {
        Customer customer = new Customer("철수", 16L);
        StoreService storeService = new StoreService(customerService);
        when(customerService.findByName("철수")).thenReturn(customer);
        String result = storeService.sellCigar("철수");
        assertEquals("미성년자에게는 판매할 수 없습니다.", result);
    }

BDD 방식

위 테스트 로직을 BDD 스타일로 수정한다면 새로운 메서드를 사용하게 되는데 import문을 보면 차이를 알 수 있다.

// 일반
import static org.mockito.Mockito.*;
// BDD
import static org.mockito.BDDMockito.*;

BDD Mockito가 Mockito를 상속받아 구현되었기 때문에 BDD Mockito만 임포트하여도 된다.

    @Test
    @DisplayName("미성년자 담배 구매 - BDD 방식")
    void testBdd() {
        // given
        Customer customer = new Customer("철수", 16L);
        StoreService storeService = new StoreService(customerService);
        /// mocking
        given(customerService.findByName("철수")).willReturn(customer);

        // when
        String result = storeService.sellCigar("철수");

        // then
        assertEquals("미성년자에게는 판매할 수 없습니다.", result);
    }

코드를 보면 given - when - then으로 구분하여 작성된 걸 확인할 수 있다.
기본 방식에서 mocking을 when()을 사용하였는데 BDD 방식의 when단계와 다르며 기존 when은 given단계에 해당된다.
그래서 기존 when(...).thenReturn(...)을 given(...).willReturn(...)으로 mocking한다.
BDD로 작성된 코드를 문장으로 풀어보면 정의된 시나리오대로 BDD 단계에 맞게 수행되는 것을 알 수 있다.
사실 기본 방식을 보면 BDD 방식과 매우 흡사하다고 생각이 든다. 하지만 시나리오와 수행하는 행동을 기준으로 본다면 좀 더 자연스럽게 테스트 코드의 진행이 느껴진다.
BDD 방식이 TDD를 기반으로 만들어졌기 때문에 BDD에 TDD가 포함된다고 한다. Mockito에 한정하지 말고 BDD와 TDD의 개념에 대해 좀 더 찾아보고 따로 포스팅을 해야겠다.

'개발 > Test' 카테고리의 다른 글

[Test] Mockito를 사용해보자 (1)  (0) 2022.02.10
[Test] JUnit5을 사용해보자 (2)  (0) 2022.02.10
[Test] JUnit5을 사용해보자 (1)  (0) 2022.02.10