세번째 속성인 격리 문제의 차이가 고전파와 런던파를 구분할 수 있게 해주는 근원
격리 주체 | 단위의 크기 | 테스트 대역 사용 대상 | |
---|---|---|---|
런던파 | 단위 | 단일 클래스 | 불변 의존성 외 모든 의존성 |
고전파 | 단위 테스트 | 단일 크랠스 또는 클래스 세트 | 공유 의존성 |
테스트는 코드의 단위를 검증해서는 안 된다. 오히려 동작의 단위, 즉 문제 영역에 의미가 있는 것, 이상적응로는 비즈니스 담당자가 유용하다고 인식할 수 있는 것을 검증해야 한다.
class CustomerClassicTest {
@Test
public void purchase_succeeds_when_enough_inventory() {
// given
Store store = new Store();
store.addInventory(Product.SHAMPOO, 10);
Customer customer = new Customer();
// when
boolean success = customer.purchase(store, Product.SHAMPOO, 5);
// then
assertThat(success).isTrue();
assertThat(5).isEqualTo(store.getInventory(Product.SHAMPOO));
}
@Test
public void purchase_fails_when_not_enough_inventory() {
// given
Store store = new Store();
store.addInventory(Product.SHAMPOO, 10);
Customer customer = new Customer();
// when
boolean success = customer.purchase(store, Product.SHAMPOO, 15);
// then
assertThat(success).isFalse();
}
}
이 코드는 단위 테스트의 곡전 스타일 예로, 테스트는 협력자(Store 클래스)를 대체하지 않고 운영용 인스턴스를 사용하기 때문에 Customer와 Store 둘 다 효과적으로 검증한다. 하지만 Customer가 올바르게 작동하더라도 Customer에 영향을 미치는 Store 내부에 버그가 있으면 단위 테스트에 실패할 수 있다. 결국 테스트에서 두 클래스는 서로 격리돼 있지 않다.
@ExtendWith(MockitoExtension.class)
class CustomerLondonTest {
@Mock
private Store store;
@Test
public void purchase_succeeds_when_enough_inventory() {
// given
given(store.hasEnoughInventory(Product.SHAMPOO, 5)).willReturn(true);
Customer customer = new Customer();
// when
boolean success = customer.purchase(store, Product.SHAMPOO, 5);
// then
assertThat(success).isTrue();
then(store)
.should(times(1))
.removeInventory(Product.SHAMPOO, 5);
}
@Test
public void purchase_fails_when_not_enough_inventory() {
// given
given(store.hasEnoughInventory(Product.SHAMPOO, 5)).willReturn(false);
Customer customer = new Customer();
// when
boolean success = customer.purchase(store, Product.SHAMPOO, 5);
// then
assertThat(success).isFalse();
then(store)
.should(times(0))
.removeInventory(Product.SHAMPOO, 5);
}
}
런던스타일의 테스트는 Store의 실제 인스턴스를 생성하지 않고 Mockito를 사용해 Mock객체를 생성하였다. 또한 샴푸 재고를 추가해 Store 상태를 수정하는 대신 hasEnoughInventory() 메서드 호출에 어떻게 응답하는지 목에 직접 정의 한다. 사실 Store가 구체클래스인데 구체 클래스를 목으로 만드는것은 안티패턴 이지만 런던 스타일의 테스트 코드는 어떤지 고전파 스타일과 비교하기위해서 이렇게 작성하였다.