테스트주도개발 04
Updated:
테스트 주도개발 4장
4.1 Mock 객체
Mock 객체란?
- 모듈의 겉모양이 실제 모듈과 비슷하게 보이도록 만든 가짜 객체
- 실제 객체를 만들기에 비용과 시간이 많이 들거나 의존성이 긴 경우 사용
- 예를 들어 암호 저장 기능에 대한 케이스를 만들 경우 다음과 같다.
대상 문자열 | 해시 문자열 |
---|---|
potato | 8ee2027983915ec78acc45027d874316 |
- 사용자 암호 저장 기능에 대한 테스트 케이스
@Test
public void testSavePassword() throws Exception {
UserRegister register = new UserRegister();
Cipher cipher = ???? // 암호화할 클래스가 필요
String userId = "sweet88";
String password = "potato";
register.savePassword( userId, cipher.encrypt(password) );
String decryptedPassword = cipher.decrypt(register.getPassword(userId));
assertEquals(password, decrytedPassword);
}
- 테스트 케이스에 사용할 수 있도록 동작을 흉내 내어 구성한 MockMD5Cipher 클래스
public class MockMD5Cipher implements Cipher {
@Override
public String decrypt (String source) {
return "potato";
}
@Override
public String encrypt (String source) {
return "8ee2027983915ec78acc45027d874316"; // 강제적으로 예상 결과 값을 넣어줌
}
}
- savePassword 메소드 구현을 시작할 수 있도록 준비를 마친 테스트 케이스
@Test
public void testSavePassword() throws Exception {
UserRegister register = new UserRegister();
Cipher cipher = new MockMD5Cipher();
String password = "potato";
String userId = "sweet88";
register.savePassword(userId, cipher.encrypt(password));
String decryptedPassword = cipher.decryption(register.getPassword(userId));
assertEquals(password, decryptedPassword);
}
언제 Mock 객체를 만들 것인가?
- 의존성이 근본적인 원인
- 테스트 작성을 위한 환경 구축이 어려워서 ex) DB설치, 웹서버 설치, 방화벽 등
- 테스트 특정 경우나 순간에 의존적이라서 ex) 예외 처리
- 테스 시간이 오래 걸려서 ex) 모듈 호출시 수행 결과가 오래 걸릴 경우
4.2 Mock에 대한 기본적인 분류 개념, 테스트 더블
- 제라드 메스자로스가 만들어낸 용어 테스트 더블
- 테스트 더블은 대역, 스턴트 맨을 의미하는 스턴트 더블에서 차용
-
오리지널 객체를 이용해서 테스트가 어려울 경우 이를 대신 해서 진행하도록 만들어주는 객체를 지칭
- Mock 객체와 의미가 겹치는데 제라드는 Mock 객체를 좀 더 프레임 워크와 밀접한 형태로 설명하면서 테스트 더블의 하위로 분류함
- 그러나 대부분 그냥 포괄적이고 보편적으로 Mock이라고 씀. 그냥 알아두면 좋다의 느낌
- 위에 사진을 설명하기 위한 예제로 초기 설정은 다음과 같다.
public interface ICoupon {
public String getName(); // 쿠폰 이름
public boolean isValid(); // 쿠폰 유효 여부 확인
public int getDiscountPercent(); // 할인율
public boolean isAppliable(Item item); // 해당 아이템에 적용 가능 여부
public void doExpire(); // 사용할 수 없는 쿠폰으로 만듦
}
- 테스트 시나리오는 다음과 같다.
유저 ID를 기준으로 신규유저 객체를 생성
→ 현재 쿠폰 확인
→ 신규 쿠폰 받기
→ 유저의 보유 쿠폰 개수 확인
(우선은 보유 쿠폰 수가 증가됐는지 판단하는 수준에서 작성)
- TDD 방식으로 테스트 구현
public class UserTest {
@Test
public void testAddCoupon() throws Exception {
User user = new User("area88");
assertEquals("쿠폰 수령 전", 0, user.getTotalCouponCount());
ICoupon coupon = ?????? // 아! 여길 어떻게 처리해야 하지?
user.addCoupon(coupon);
assertEquals("쿠폰 수령 후", 1, user.getTotalCouponCount());
}
}
1. 더미 객체 (Dummy Object)
- 오로지 인스턴스화 될 수 있는 수준으로만 ICoupon 인터페이스를 구현한 객체
- 단순 인스턴스화 된 객체가 필요할 때만 사용. 기능은 필요 없음
-
따라서 더미 객체 메소드를 호출 할 때는 정상 동작이 보장되지 않음
- 위의 TDD 예제를 통과 하기 위해 임의 클래스를 만듬
public class DummyCoupon implements ICoupon {
@Override
public int getDiscountPercent() {
return 0;
}
@Override
public String getName() {
return null;
}
@Override
public boolean isAppliable(Item item) {
return false;
}
@Override
public boolean isValid() {
return false;
}
@Override
public void doExpire() {
}
}
- 이후 다시 작성된 UserTest 클래스의 testAddCoupon 테스트 메소드
@Test
public void testAddCoupon() throws Exception {
User user = new User("area88");
assertEquals(0, user.getTotalCouponCount());
ICoupon coupon = new DummyCoupon(); // 완성
user.addCoupon(coupon);
assertEquals(1, user.getTotalCouponCount());
}
2. 테스트 스텁(Test Stub)
- 더미 객체에서 생성된 메소드를 사용해야 할 경우 사용
- 즉, 더미 객체가 마치 실제로 동작하는 것처럼 보이게 만들어 놓은 객체
-
4.1의 예제 MockMD5Cipher가 이에 해당.
- 예제는 다음과 같다. DummyCoupon 클래스와 같지만 단순 return 값을 넣어줌
public class StubCoupon implements ICoupon {
@Override
public int getDiscountPercent() {
return 7;
}
@Override
public String getName() {
return "VIP 고객 한가위 감사쿠폰";
}
@Override
public boolean isAppliable(Item item) { return true; }
@Override
public boolean isValid() { return true; }
@Override
public void doExpire() { }
}
- 위와 같이 만든 코드를 테스트 더블 or 테스트 스텁이라 부름
-
“VIP 고객 한가위 감사쿠폰”을 테스트 픽처스로 사용할 수 있게 됨.
- 그러나 위는 한 가지 경우에 대해서만 통과. 여러 개의 경우에 대해선 어떻게 할 것인가?
3. 페이크 객체(Fake Object)
- 스텁은 하나의 인스턴스를 대표할 때 쓰인다
-
페이크는 여러 개의 인스턴스를 대표할 때 쓰인다. (더 복잡한 구현이 들어가 있는 객체)
- 아래는 페이크 객체의 방식을 그림으로 표현
- 너무 구현을 하다보면 페이크 객체 자체를 테스트 해야할 정도로 복잡해짐
- 따라서 페이크 객체를 만들 때는, 적당히 구현. 아니면 Mock 프레임워크를 사용.
4. 테스트 스파이(Test Spy)
- 특정 메소드의 정상호출 여부 확인을 목적으로 구현
- 더미부터 시작해서 페이크 객체이르기 까지 테스트 더블로 구현된 객체 전 범위의 기능을 추가 할 수 있음
- 원하는 동작을 하면서 다른 동작을 함
- isAppliable 메소드가 1번 호출되어서 계산에 사용되어야 한다는 가정으로 예시는 다음과 같다.
@Test
public void testGetOrderPrice_discountableItem() throws Exception {
PriceCalculator calculator = new PriceCalculator();
Item item = new Item("LightSavor","부엌칼",100000);
ICoupon coupon = new FakeCoupon();
assertEquals("쿠폰 할인된 가격", 93000, calculator.getOrderPrice(item,coupon));
}
- 여기서 isAppliable 메소드 호출 횟수 확인해보고 싶다. 그래서 아래와 같이 SpyCoupon을 구현
public class SpyCoupon implements ICoupon {
List<String> categoryList = new ArrayList();
private int isAppliableCallCount;
@Override
public boolean isAppliable(Item item) {
isAppliableCallCount++; // 호출되면 증가
if(this.categoryList.contains(item.getCategory())) {
return true;
}
return false;
}
public int getIsAppliableCallCount(){ // 몇 번 호출됐나?
return this.isAppliableCallCount;
}
}
- getIsAppliableCallCount() 메소드의 경우 스펙에 없어도 만들어도 되는 이유는 어차피 가짜 객체이고 테스트를 위해 만든 것이므로 크게 상관 없다.
@Test
public void testGetOrderPrice_discountableItem() throws Exception {
PriceCalculator calculator = new PriceCalculator();
Item item = new Item("LightSavor","Kitchen knife",100000);
ICoupon coupon = new SpyCoupon();
assertEquals("쿠폰할인된 가격", 93000, calculator.getOrderPrice(item, coupon));
int methodCallCount = ((SpyCoupon)coupon).getIsAppliableCallCount();
assertEquals("coupon.isAppliable 메소드 호출 횟수", 1, methodCallCount);
}
- 이렇게 스냅샷 같은 감시가 필요한 경우에 유용하게 사용.
- 보통은 잘 안씀. Mock 프레임워크가 강력, 편하고 스파이 기능도 제공함.
** 상태기반 테스트 vs 행위 기반 테스트 **
- 상태 기반 테스트란 테스트 대상 클래스의 메소드를 호출하고, 그 결과값과 예상값을 비교하는 방식
- 예를 들어서 setName(“신선호”)를 호출하면 객체의 속성이 변경된다. 이때 객체의 속성이 원하는 값으로 변경되었는지 getName()으로 확인해보는 방식이다.
-
댄 노스가 창시자
- 행위 기반 테스트는 올바른 로직 수행에 대한 판단의 근거로 특정한 동작의 수행 여부를 이용한다.
- 보통 메소드의 리턴값이 없거나 리턴값을 확인하는 것만으로는 예상 동작 했음을 보증하기 어려울때 사용
- 위의 그림처럼 methodA에 A가 입력되면 methodB는 호출되지 않아야 정상이다. 그리고 B가 입력되면 methodB가 호출돼야 정상이다. 하지만 테스트 대상이 되는 methodA만 놓고 봤을 때는 입력 값에 따른 차이를 methodA만으로는 알아낼 수가 없다. 즉 methodB의 호출 여부를 조사하지 않으면, methodA의 정상 동작 여부를 판단할 수 없다. 만일 methodA가 정상 동작했을 경우 methodB가 반드시 호출되는 구성이라면, 반대로 methodB의 호출 여부로 methodA의 정상 여 부를 판단할 수 있다고 보는 것이다. 따라서 이럴 때는 methodB의 호출 여부를 확인하는 것이 테스트 시나리오의 종료조건이 된다. 하지만 전통적인 테스트 케이스 작성 방식, 즉 상태 기반 테스트에선, 사실상 이런 상황에 대한 테스트 케이스를 작성하기가 매우 어렵거나 불편했다. 테 스트 대상인 A가 상태를 갖고 있지 않기 때문이다.
- 이건 긁어와야 했다…. 이렇게 봐야 이해
- 이럴 때 사용하는 것이 스파이 객체 or Mock 객체를 따로 만들어서 테스트 케이스 작성
- 따라서 예상하는 행위들을 미리 시나리오로 만들어놓고 해당 시나리오대로 동작이 발생했는지 여부를 확인하는 것이 핵심
** 상태 기반으로 가능하면 굳이 행위 기반으로 하지 않는 것이 좋다 **
5. Mock 객체
일반적인 테스트 더블은 상태(state)를 기반으로 테스트 케이스를 작성한다.
Mock 객체는 행위(behavior)를 기반으로 테스트 케이스를 작성한다.
위의 쿠폰 예제에 ‘유저가 쿠폰을 받을 때, 쿠폰이 유효한 상태일 경우에만 다운 받을 수 있다’는 규칙을 추가
- 이 규칙으로 user.addCoupon(coupon) 메소드가 정상 호출 되었다면
- 반드시 coupon.isValid()도 함께 호출되어, 쿠폰의 유효 여부를 확인하는 과정이 필요.
- 그리고 나서 해당 쿠폰을 취득할수 있게 해야함.
- 다음은 일반적인 상태 기반 테스트 케이스
ICoupon coupon = new FakeCoupon();
user.addCoupon(coupon);
assertEquals(1, user.getTotalCouponCount());
- 상태(1이라는 값)가 정상적으로 보이는지 확인한다.
- 문제는 추가된 규칙이 addCoupon에 들어있는지 그리고 정상적으로 호출되어 사용되었는지 확인할 방법이 없음.
- 아래는 jMock 라이브러리를 사용한 예제. 오래된 문법이므로 뉘앙스를 느껴라.
ICoupon coupon = mock(ICoupon.class);
coupon.expects(once()).method("isValid") // ➊
.withAnyArguments() // ➋
.will(returnValue(true)); // ➌
user.addCoupon(coupon);
assertEquals(1, user.getTotalCouponCount());
- ➊ Mock으로 만들어진 coupon 객체의 isValid 메소드가 한 번 호출될 것을 예상함
- ➋ isValid에서 사용할 인자는 무엇이 됐든 상관 안 함
- ➌ 호출 시에 리턴값은 true를 돌려주게 될 것임
Mock 객체 용어가 주는 혼란스러움 정리
- Mock 객체라는 단어의 의미는 제라드의 분류법 보단 ‘가상 임시 구현체’의 의미로 사용되는 경우가 더 많음.
- 보통 테스트 더블과 Mock 객체가 동등한 의미로 사용
- 그래서 이 책에서는 같은 의미로 사용. 단지 이 장에서는 제라드 분류법으로 설명한 것 뿐
- Mocking의 의미도 테스트 더블 객체를 만드는 것을 의미로 생각하자.
- Mock 프레임워크로 클래스 하나 인스턴스화하면 더미 객체와 동일
** 테스트 더블 익명 클래스로 만들기 **
- 매번 새로운 더미 객체를 만들기 위해서 클래스를 만들어야 한다.
- 결국 클래스 개수가 늘어남. 관리하는 소스가 늘어난다는 이야기
- 이때 익명 클래스를 만들면 좋다.
@Test
public void testAddCoupon() throws Exception {
User user = new User("area88");
assertEquals("최초 쿠폰 수", 0, user.getTotalCouponCount());
ICoupon coupon = new ICoupon(){
@Override
public void doExpire() {
}
@Override
public int getDiscountPercent() {
return 0;
}
@Override
public String getName() {
return null;
}
@Override
public boolean isAppliable(Item item) {
return false;
}
@Override
public boolean isValid() {
return false;
}
};
user.addCoupon( coupon );
assertEquals("쿠폰 수령 후", 1, user.getTotalCouponCount());
}
- 이렇게 만들어 놓고 필요한 내용 넣으면 스텁도 되고 페이크도 되고 스파이도 된다.
- 더 필요하다면 coupon을 필드 변수로 뽑아 내고 익명 클래스로 만든다. 그리고 인터페이스를 구현하고 있는 부분을 setUp 메소드로 이동시키면 더 깰끔.
4.3 프레임워크
- Mock 객체를 직접 작성해서 명시적인 클래스 만들지 않아도 됨
- Mock 객체에 대해서 행위까지도 테스트 케이스에 포함 가능
- jMock, EasyMock, Mockito등 여러가지가 있다.
- 책에는 jMock, EasyMock의 간단한 사용법이 있으나 나는 Mockito만 보겠다.
고전주의 TDD 개발자 vs Mock 주도 개발자
-
Mock은 행위 기반 테스트 같은 곤란한 상황에 대한 테스트도 지원해서 좋다.
- 그러나 고전 상태 기반 테스트을 지지하는 사람들이 싫어(?)했다?ㅋㅋㅋ
- 저자는 상태기반 테스트가 좋다고 생각
- 근데 역시나 상황에 따라 사용이 다르다는게 중립 기어
Mockito
- 최근 떠오르는 Mock 프레임워크
- 수제빵 파베르가 개발
Mock 프레임워크는 어때야 하는가?
- 단순해야함.
- DSL로 만들지 말자. (특정 문제 영역을 해결하기 위해 만들어진 언어나 스펙. 결국 테스트를 위해 또 다른 언어나 스펙을 만들면 복잡해진다라는 뜻)
- 문자열을 메소드 대신 사용하지 말자
- 익명 inner 클래스 사용하지 말자
- 리펙토링이 어려워서 안된다.
Mockito 차별점은?
- 테스트 그 자체에 집중
- 테스트 스텁을 만드는 것과 검증을 분리시켰다.
- Mock 만드는 방법을 단일화했다.
- 테스트 스텁을 만들기 쉽다.
- API가 간단하다
- 프레임워크가 지원해주지 않으면 안 되는 코드를 최대한 배제
- 실패 시에 발생하는 에러추적이 깔끔하다.
1. Mock 객체 만들기
// Mockito.mock(타깃 인터페이스);
import static org.mockito.Mockito.*;
List mockedList = mock(List.class);
System.out.println(mockedList.size()); // 결과 값 0
- 리턴 타입(return type)의 기본값으로 동작한다. int는 0, boolean은 false, 클래스 타 입들은 null을 돌려줌
2. 예상값 지정
- 예상 값을 지정하는 것이 아니라 필요시에 스텁만 만들고 나중에 호출 여부를 확인하는 방식
-
기존에는 스텁 → 예상 → 수행 → 검증
- Mockito는 스텁 → 수행 → 검증 이다.
- 그래서 Mockito는 예상값 지정 부분이 없다.
3. 테스트에 사용할 스텁 만들기
- 스텁은 필요할 때만 만드는 것이 원칙
- 메소드 호출 여부를 검증만 할 때는 사용하지 않는다.
when(Mock 객체의 메소드).thenReturn(리턴값); when(Mock 객체의 메소드).thenThrow(예외);
List mockedList = mock(List.class);
// Mock 객체를 사용한다.
mockedList.add("item");
mockedList.clear();
// 검증
verify(mockedList).add("item");
verify(mockedList).clear();
- 위와 같이 Mock 객체를 생성만 하고, 나중에 호출을 검증하는 식으로 테스트 코드가 작성
- 만일 테스트를 위해 Mock 객체의 특정 메소드를 사용해야 할 필요가 생기면 그때 만든다.
// Stub 만들기
when(mockedList.get(0)).thenReturn("item");
when(mockedList.size()).thenReturn(1);
when(mockedList.get(1)).thenThrow(new RuntimeException());
System.out.println(mockedList.get(0));
System.out.println(mockedList.size());
System.out.println(mockedList.get(2));
System.out.println(mockedList.get(1));
//-----실행 결과-----
Item
1
null
Java.lang.RuntimeException at SimpleMockTest.testMockList(SimpleMockTest.Java:25)
4. 검증
verify(Mock 객체).Mock 객체의 메소드;
verify(Mock 객체, 호출횟수지정 메소드).Mock 객체의 메소드;
호출횟수 지정 메소드 | 설명 |
---|---|
times(n) | n번 호출됐는지 확인. n=0은 times를 지정하지 않았을 때와 동일하다. |
never | 호출되지 않았어야 함 |
atLeastOnce | 최소 한 번은 호출됐어야 함 |
atLeast(n) | 적어도 n번은 호출됐어야 함 |
atMost(n) | 최대 n번 이상 호출되면 안 됨 |
- 예제
verify(mockedList).add("item");
verify(mockedList, times(1)).add("item");
verify(mockedList, times(2)).add(box);
verify(mockedList, never()).add(car);
verify(mockedList, atLeastOnce()).removeAll();
verify(mockedList, atLeast(2)).size();
verify(mockedList, atMost(5)).add(box);
import static org.mockito.Mockito.*;
public class SimpleMockTest {
@Test
public void testMockList() throws Exception {
List mockedList = mock(List.class);
verify(mockedList).size();
}
}
//-----실행 결과-----
Wanted but not invoked:
list.size();-> at SimpleMockTest.testMockList(SimpleMockTest.Java:19)
Actually, there were zero interactions with this mock.
a. 검증에서 인자 값으로 사용되는 Argument Matcher
예시: verify(mockedList, times(5)).add( any() );
해석: 어떤 객체가 add 메소드의 인자로 와도 무방하고 대신 5번 호출됐다면 성공이란 뜻
메소드 | 설명 |
---|---|
any 타입 | anyInteger( ), anyBoolean 등 Java 타입에 해당하는 any 시리즈가 있다. 이런 시리즈들은 null이거나 해당 타입이면 만족한다 |
anyCollection anyCollectionOf |
List, Map, Set 등 Collection 객체이면 무방. anyCollectionOf는 anyCollection과 동일하다. |
argThat(HamcrestMatcher) | Mockito도 Hamcrest Matcher를 사용하고 있다. Hamcrest에서 사용하던 Matcher를 그대로 이 부분에서 사용할 수 있다 |
eq | Argument Matcher가 한번 사용된 부분에선 Java의 타입을 그대로는 더 이상 쓸 수가 없다. verify(mock).add(anyString(), “item”); 즉, 위와 같이는 쓸 수 없다. 이럴 때 아래와 같이 eq를 이용한다. verify(mock).put(anyString(9), eq(“item”)); |
anyVararg | 여러 개의 인자를 지칭할 때 사용한다. 예시) // verification mock.foo(1, 2); mock.foo(1, 2, 3, 4); verify(mock, times(2)).foo(anyVararg( )); // Stubbing: when(mock.foo(anyVararg( )).thenReturn(100); |
matches(String regex) | 정규식 문자열로 인자(argument) 대상을 지칭한다. |
startswith(String) endWith(String |
특정 문자열로 시작하거나 끝나면 OK |
anyList anyCollection anySet |
anyMap의 좀 더 디테일한 버전이다. 해당 타입이기만 하면 OK |
isA(Class) | 해당 클래스 타입이기만 하면 된다 |
startswith(String) endWith(String) |
특정 문자열로 시작하거나 끝나면 OK |
isNull | Null이면 OK |
isNotNull | null만 아니면 OK |
b. 순서검증
- Stub으로 만들어진 Mock 객체 메소드의 호출 순서까지 검증하고 싶다면 InOrder 클래스를 이용한다.
List firstMock = mock(List.class);
List secondMock = mock(List.class);
firstMock.add("item1");
secondMock.add("item2");
InOrder inOrder = inOrder(firstMock, secondMock);
inOrder.verify(firstMock).add("item1");
inOrder.verify(secondMock).add("item2");
- add 메소드의 호출이 item1 → item2 순으로 이뤄지지 않았다면 실패로 간주
Mockito의 특징적인 기능
1. void 메소드를 Stub으로 만들기
doThrow(예외).when(Mock_객체).voidMethod();
- void 메소드는 리턴이 없어서 stub을 만들 일이 없다.
- 단, 예외 발생하는 경우 Stub으로 구현해야 함.
- 이때 doThrow를 사용
doThrow(new RuntimeException()).when(mockList).clear();
2. 콜백으로 Stub 만들기 : thenAnswer
- Mock은 보통 하드코딩된 값만 돌려줌
- 그러나 특정 Mock 메소드에 대해 실제 로직을 구현하고자 할때 콜백 기법을 사용
- 그러나 TDD 자체에서 권장하지 않음
when(rs.getInt("no")).thenAnswer( new Answer<Integer>(){
public Integer answer(InvocationOnMock invocation) throws Throwable {
if (currentRecord = = null)
throw new SQLException("access fields is empty");
return ((Integer)currentRecord[0]).intValue();
}
});
3. 실체 객체를 Stub으로 만들기 : SPY
- 실 객체도 Mock으로 만들 수 있다.
- 강력한 나머지 가끔 문제가 될 수 도 있다.
- 서드파티(3rd Party) 제품, 고칠 수 없는 라이브러리만 남아 있는 코드에서만 사용
-
모키토 저자는 왠만하면 spy 기능을 쓰는건 잘못됐다고 한다.
- 참고로 final로 선언된 메소드는 stub으로 만들 수 없다.
ArrayList<String> realList = new ArrayList<String>();
realList.add("Hello");
System.out.println(realList.get(0));
List mockedList = spy( realList );
when(mockedList.get(0)).thenReturn("item");
System.out.println(mockedList.get(0));
//-----실행 결과-----
Hello
item
4. 똑똑한 NULL 처리 : SMART NULLS
- 스텁으로 만들지 않은 기본형 외의 값을 리턴하는 메소드는 null 값이 찍힌다.
- 이렇게 만들어진건 null 에러를 유발
- 필요에 따라 유용하게 기본값으로 찍히게 만드는 것이 SMART NULLS라는 방식
List mockedList = mock(List.class);
List smartMockedList = mock(List.class, RETURNS_SMART_NULLS);
System.out.println(mockedList.toArray());
System.out.println(smartMockedList.toArray());
//-----실행 결과-----
null
[LJava.lang.Object;@3eca90
SMART NULLS 규칙
- 기본형 래퍼 클래스는 해당 기본형 값으로 바꾼다.
- String은 ““로 바꾼다.
- 배열은 크기 0인 기본 배열 객체로 만들어준다.
- Collection 계열은 빈 Collection 객체로 만든다.
5. 행위 주도 개발(BDD) 스타일 지원
- Mockito는 //given //when //then 식의 행위 주도 개발(Behavior-Driven Development, BDD) 스타일로 테스트 케이스 작성할 수 있게 지원
- BDD 스타 일을 사용하려면 Mockito 클래스 대신 BDDMockito를 static import 한다
import static org.mockito.BDDMockito.*;
Seller seller = mock(Seller.class);
Shop shop = new Shop(seller);
public void shouldBuyBread() throws Exception {
// given
given(seller.askForBread()).willReturn(new Bread());
// when
Goods goods = shop.buyBread();
// then
assertThat(goods, containBread());
}