1. 테스팅이란 무엇인가
테스팅의 목표
- 소프트웨어의 지속가능한 성장
- 더 좋은 소프트웨어 디자인을 이끌어줌
테스트 커버리지?
- 코드 커버리지 = 테스트로 실행된 코드 라인수 / 전체 코드 라인수
- 커버리지는 몇퍼를 만족해야하나?
- 커버리지가 60% 아래면 보통 문제가 발생할 여지가 있음
- 60%보다 크다고해서 많은 의미가 있다고 볼 수도 없음 (과하다)
좋은 테스트란
- 개발 사이클에 들어가 있는 테스트를 지향해야함 (Build pipeline/script)
- 코드 중 중요한 코드들을 선별하여 테스팅하는 것이 좋음 ex) 도메인 모델
- 좋은 테스트를 작성하는 것이 최소 비용으로 최대 이득을 얻을 수 있음.
2. 테스트의 종류
- 크게
Unit tests
, Integration test
, End-to-End test
가 있다.
- 뒤로갈수록 고립도는 적어지고 통합도가 높아짐
- 뒤로갈수록 테스트 속도는 느려짐
단위 테스트 (Unit Test)
- 코드의 작은 부분을 빠르게 테스트 하는 용도
- 격리된 방식으로 처리하는 자동화된 테스트
London School vs Classical School?
- 런던파, 고전파는 위 설명에서 격리를 바라보는 관점이 다르다.
- 런던파의 테스트 격리
- 테스트 대상 시스템 (System under test, SUT)를 협력자에게서 격리한다.
- 예를 들어, A 클래스를 테스트 한다고 했을 때, A클래스가 B,C클래스에 의존한다면 이러한 의존성들을 테스트 대역(Test Double)로 대체한다.
- 특정 클래스의 동작을 외부 영향과 격리해서 대상 클래스에만 집중할 수 있도록함.
- 테스트가 실패하면 의존성이 없기 때문에 쉽게 문제점을 발견할 수 있다.
- 고전파의 테스트 격리
- 단위 테스트 간에 격리를 수행한다. (클래스를 격리하지 않는다)
- 각각의 클래스들 간의 멤버 변수를 공유하거나 영향이 없다면 한 번에 테스트 가능하다.
- 런던파, 고전파 코드 예시
@Test
void Purchase_succeeds_when_enough_inventory() {
Store storeMock = Mockito.mock(Store.class);
when(storeMock.hasEnoughInventory(Product.SHAMPOO, 5))
.thenReturn(true);
Customer customer = new Customer();
boolean success = customer.purchase(storeMock, Product.SHAMPOO, 5);
assertThat(success).isTrue();
verify(storeMock, times(1)).removeInventory(Product.SHAMPOO, 5);
}
@Test
void Purchase_succeeds_when_enough_inventory() {
Store store = new Store();
store.addInventory(Product.SHAMPOO, 10);
Customer customer = new Customer();
boolean success = customer.purchase(store, Product.SHAMPOO, 5);
assertThat(success).isTrue();
assertThat(store.getInventory(Product.SHAMPOO)).isEqualTo(5);
}
통합 테스트 (Integration Test)
- 대부분의 고전파 테스트는 통합 테스트로 간주된다.
- 여러 클래스를 동시에 테스트
End-to-End 테스트
- 통합테스트의 하위 집합으로 볼 수 있다.
- 공유 의존성뿐만 아니라 조직 내 다른 팀이 개발한 코드 등과 통합해 작동하는 테스트이다.
- 일반 통합 테스트보다 더 많은 의존성이 존재하며 가장 많은 비용이 발생한다.