테스트
왜 테스트해야 할까?
- 테스트란 소프트웨어 개발 핵심 과정 중 하나로 코드의 안정성과 기능성 및 성능을 검증하는 데 필수적이다.
- 테스트를 통해 재배포, 재개발 비용 감소하며 코드 품질이 높아져 유지보수가 더욱 쉬워진다는 장점이 있다.
어떤 테스트를 얼마나 작성하지?
- 테스트 피라미드는 테스트의 다양한 유형을 정의하여 중요도와 범위를 시각적으로 나타내는 모델이다. 테스트 범위와 속도 등을 고려하여 어떤 테스트를 얼마나 수행해야 하는지 균형을 제시한다.
- 핵심 원칙은 상단의 테스트는 적게, 하단의 테스트는 많이 작성되어야 한다는 것이다.
1. 단위테스트
- 단위 테스트는 가장 아래에 위치하며, 가장 작은 단위의 코드를 테스트한다.
- 독립적으로 빠르게 실행되며 이를 위해 Mock 객체나 Stub을 사용할 수 있다.
2. 서비스 테스트
- 서비스 테스트는 통합 테스트라고도 하며, 서로 다른 컴포넌트나 시스템들이 함께 잘 작동하는지 확인한다.
3. UI 테스트
- UI 테스트는 앤드 투 앤드 테스트라고도 하며, 사용자의 관점에서 시스템의 전체 흐름을 검증한다.
- 전체 테스트 중 가장 적은 수로 이루어지며, 테스트 실행에 가장 긴 시간이 걸린다.
좋은 테스트를 위한 FIRST 원칙
- Fast: 테스트는 지속적이고 빠른 피드백을 줄 수 있어야 한다.
- Independent: 테스트들은 서로 독립적이어야 한다.
- Repeatable: 어떤 환경에서도 테스트는 반복할 수 있어야 한다.
- Self-validating: 스스로 검증할 수 있어야 한다.
- Timely: 적절한 시기에 테스트를 작성해야 한다.
테스트 관련 용어
1. 목(Mock) 객체
- 실제 객체를 흉내 내는 가짜 객체로 주로 단위 테스트에서 사용된다.
- 다음과 같은 목적을 가진다.
- 의존관계 분리: 실제 서비스나 데이터베이스에 의존하는 경우 Mock 객체로 대체하여 테스트 환경 설정의 복잡성을 줄인다.
- 상태, 행위 검증: 특정 상태나 특정 상황을 Mock 객체로 쉽게 만들어 모의 테스트를 할 수 있게한다.
- 속도: 외부 API나 데이터베이스를 사용할 경우 발생하는 지연 시간을 줄일 수 있다.
- 예를 들어 테스트 중인 코드가 Mock 객체와 예상대로 상호작용 하는지를 검증한다.
2. 스텁(Stub)
- 특정 테스트를 위한 고정된 데이터를 제공하며 특정 상황을 모의하는 데 사용된다.
- 예를 들어 특정한 메서드 호출 시 항상 동일한 값을 반환하도록 설정하는 것이다.
3. 테스트 대역(Test Double)
- 실제 구현대신 사용되는 객체를 총칭하는 용어이다. Mock, Stub, Dummy, Fake, Spy를 사용함으로써 테스트의 유연성을 높이고 다양한 시나리오를 효과적으로 테스트한다.
3. 더미(Dummy)
- 객체는 전달되지만 실제로는 사용되지 않는 객체를 말한다. 주로 매개변수 목록을 채우는 데 사용된다.
4. 가짜(Fake)
- 메모리 데이터베이스처럼 실제로 작동하는 구현 객체가 있지만 실제 배포에는 적합하지 않는 객체를 말한다.
5. 스파이(Spy)
- 호출되면서 호출된 메서드에 대한 정보를 일정한 방식으로 기록하는 객체를 말한다.
JUnit
- Junit이란 Java를 위한 대표적인 단위 테스팅 프레임워크이다.
JUnit 생명 주기
- Junit 테스트의 시작부터 종료까지의 과정을 말한다. 각 테스트마다 필요환 환경을 준비하고 테스트가 끝나면 사용한 리소스를 해제하여 각 테스트간의 독립성을 보장한다.
@Beforeach
- 각 테스트 실행 전에 수행되는 작업을 정의한다.
@AfterEach
- 각 테스트 메서드 실행 후에 수행되는 작업을 정의한다.
@BeforeAll, @AfterAll
- 테스트 클래스의 시작과 종료 시점에 한 번씩만 수행되는 작업을 정의한다.
Assert API
- 테스트 코드에서 코드의 특정 부분이 예상한 대로 동작하는지 확인하기 위해 Assert를 사용한다. 만약 Assert로 설정한 조건이 충족되지 않으면 테스트는 실패한다.
- assertEquals(expected, actual);
- assertNotEquals(unexpected, actual);
- assertTrue(condition);
- assertFalse(condition);
- assertNull(object);
- asssertNotNull(object);
- assertSame(expected, actual);
- assertNotSame(unexpected, actual);
- assertArrayEquals(expectedArray, actualArray);
- assertThrows(expectedException, executable);
AssertJ
- Junit은 Java를 위한 대표적인 단위 테스트 프레임워크인 반면, AssertJ는 Fluent Assertion API를 통해 테스트 검증을 더 읽기 쉽고 자연스럽게 작성할 수 있도록 도와주는 자바 라이브러리이다.
org.junit.jupiter.api.Assertions.assertEquals(10, ret);
Assertions.assertThat(ret).isEqualTo(10);
- AssertThat 이후 다양한 조건으로 메소드 체이닝을 할 수 있어 유연한 검증이 가능하다.
Assertions.assertThat("The Lord of the Rings").isNotNull()
.startsWith("The")
.contains("Lord")
.endsWith("Rings");
- 다양한 체이닝 메소드
- .isEqualTo
- .isNotNull
- .isGreaterThan
- .contains
- .startsWith
- .endsWith
Given-When-Then 패턴
- 테스트의 구조를 명확하게 나타내기 위해 사용된다. 테스트 준비, 실행, 검증 단계로 구분할 수 있다.
@DisplayName("자연수 더하기")
@Test
void addNumbersReturnNumber() {
Calculator calc = new Calculator();
int ret = calc.add(5, 5);
Assertions.assertThat(ret).isEqualTo(10);
}
- 위의 예시에는 계산기 객체가 주어지고(given), 덧셈으로 두 자연수를 더한 뒤(when) 결과값이 10이 맞는지 검증(then)하는 코드이다.
참고자료