7.1. 테스트 피라미드

비용이 많이 드는 테스트는 지양하고 비용이 적게 드는 테스트를 많이 만들어야 한다.

비용이 많이 드는 테스트는 지양하고 비용이 적게 드는 테스트를 많이 만들어야 한다.

기본 전제는 만드는 비용이 적고, 유지보수하기 쉽고, 빨리 실행되고, 안정적인 작은 크기의 테스트들에 대해 높은 커버리지를 유지해야 한다는 것이다. 이 테스트는 하나의 ‘단위’(일반적으로 하나의 클래스)가 제대로 동작하는지 확인할 수 있는 단위 테스트들이다.

여러 개의 단위와 단위를 넘는 경계, 아키텍처 경게, 시스템 경계를 결합하는 테스트는 만드는 비용이 더 비싸지고, 실행이 더 느려지며 깨지기 더 쉬워진다. 테스트 피라미드는 테스트가 비싸질수록 테스트의 커버리지 목표는 낮게 잡아야 한다는 것을 보여준다. 그렇지 않으면 새로운 기능을 만드는 것보다 테스트를 만드는 데 시간을 더 쓰게 되기 때문이다.

맥락에 따라 테스트 피라미드에 포함되는 계층은 달라질 수 있다. ’단위 테스트’, ‘통합 테스트’, ‘시스템 테스트’의 정의는 맥락에 따라 다르다는 것을 알아두자. (프로젝트마다 다른 의미를 가질 수 있다는 뜻)

단위 테스트는 피라미드의 토대에 해당한다. 일반적으로 하나의 클래스를 인스턴스화하고 해당 클래스의 인터페이스를 통해 기능들을 테스트한다. 만약 테스트 중인 클래스가 다른 클래스에 의존한다면 의존되는 클래스들은 인스턴스화하지 않고 테스트하는 동안 필요한 작업들을 흉내 내는 목(mock)으로 대체한다.

통합테스트는 연결된 여러 유닛을 인스턴스화하고 시작점이 되는 클래스의 인터페이스로 데이터를 보낸 후 유닛들의 네트워크가 기대한대로 잘 동작하는지 검증한다.

시스템 테스트는 애플리케이션의 UI를 포함하는 end-to-end 테스트층이 있을 수 있다.

7.2. 단위 테스트로 도메인 엔티티 테스트하기

Account 엔티티의 생타는 과거 특정 시점의 계좌 잔고(baselineBalance)와 그 이후의 입출금 내역 (activity)로 구성되어 있다.

class AccountTest {
		@Test
		void withdrawalSucceeds() {
				AccountId accountId = new AccountId(1L);
				Account account = defaultAccount()
						.withAccountId(accountId)
						.withBaselineBalance(Money.of(555L))
						.withActivityWindow(new ActivityWindow(
								defaultActivity()
										.withTargetAccount(accountId)
										.withMoney(Money.of(999L)).build(),
								defaultActivity()
										.withTargetAccount(accountId)
										.withMoney(Money.of(1L)).build()))
						.build();
						
				boolean success = account.withdraw(Money.of(555L), new AccountId(99L));
				
				assertThat(success).isTrue();
				assertThat(account.getActivityWindow().getActivities()).hasSize(3);
				assertThat(account.calculateBalance().isEqualTo(Money.of(1000L));
		}
}

특정 상태의 Account를 인스턴스화하고 withdraw() 메서드를 호출해서 성공했는지 검증하고, Account 객체의 상태에 대해 기대되는 부수효과들이 잘 일어났는지 확인하는 단순한 단위 테스트.

이런식의 단위 테스트가 도메인 엔티티에 녹아 있는 비즈니스 규칙을 검증하기 가장 적절한 방법이다.

도메인 엔티티의 행동은 다른 클래스에 거의 의존하지 않기 때문에 다른 종류의 테스트가 필요하지 않다.

7.3. 단위 테스트로 유스케이스 테스트하기

다음으로 테스트할 아키텍처 요소는 유스케이스다.

  1. SendMoney 유스케이스는 출금 계좌의 잔고가 다른 트랜잭션에 의해 변경되지 않도록 락을 건다.
  2. 출금 계좌에서 돈이 출금되고 난 후 입금 계좌에 락을 걸고 돈을 입금시킨다.
  3. 이후 두 계좌 모두 락을 해제한다.