목차
Java Mockito는 자바에서 가장 인기 있는 테스트 라이브러리 중 하나로, 그만큼 사용이 쉽다는 점에서 많은 사랑을 받고 있습니다.
오늘은 이 테스트 라이브러리의 설정에 대해 다루기보다는, 여러분의 테스트를 향상시킬 수 있는 몇 가지 팁을 제공하고자 합니다.
아래에는 간단한 테스트 파일을 포함시켰으며, 이를 통해 몇 가지 유용한 테스트 케이스를 살펴볼 예정입니다.
@ExtendWith(MockitoExtension.class)
class MyTestClass {
@Mock
private ServiceToCall service; // 테스트 대상 서비스가 호출할 서비스
@InjectMocks
private ServiceUnderTest sut; // 테스트하려는 서비스
@BeforeEach
void setup(){
//TODO 각 테스트 케이스 전에 실행될 설정을 구현하세요
}
@Test
void testMyService(){
//TODO 테스트 케이스를 구현하세요
}
}
첫번째 팁: Mockito의 “when”을 정확히 사용하기
겉보기에는 간단해 보이지만, 몇 가지 주의해야 할 점들이 있습니다. 가장 먼저, Mockito의 “when” 메서드는 테스트 대상 서비스(sut)가 동작을 수행하기 전에 사용해야 합니다. 때로는 코드가 아래와 같은 순서로 작성되는 것이 논리적으로 보일 수 있습니다:
- 서비스가 동작을 수행한다.
- “when” 문을 실행한다.
- 결과를 검증한다.
하지만, 이 순서로 코드를 작성하면 원하는 결과를 얻기 어려울 수 있습니다.
이 경우 “when” 문을 beforeEach
섹션에 포함시켜 각 테스트 실행 전에 호출되도록 설정할 수 있습니다.
@BeforeEach
void setup(){
when(service.doSomething()).thenReturn("something");
}
이 예시에서는, “sut”가 서비스의 “doSomething” 메서드를 호출하면, 해당 서비스는 “something”을 반환하게 됩니다. 이 반환 값은 함수의 기대 결과와 일치해야 합니다.
만약 테스트 케이스에서 “doSomething” 함수가 호출되지 않는다면, Mockito는 오류를 발생시킬 것입니다. 이는 테스트 케이스를 디버깅하는 데 매우 유용한 도구가 됩니다.
때로는 특정 테스트 케이스에서만 필요한 “when” 문이 있을 수 있으며, 다른 테스트에서는 필요하지 않을 수도 있습니다. 이럴 때는 lenient.when
을 사용할 수 있으며, 메서드가 호출되지 않더라도 오류가 발생하지 않습니다. 그러나 lenient
에 의존하면 테스트 실패를 감지하지 못할 수 있으므로 주의가 필요합니다. 따라서, 테스트 케이스를 최대한 깔끔하게 유지하는 것이 중요합니다.
다음 팁을 통해 이 목표를 달성할 수 있습니다.
두번째 팁: 중첩 테스트 클래스를 활용하기
중첩 클래스는 여러 클래스를 하나의 테스트 케이스 내에서 효율적으로 관리할 수 있는 좋은 방법입니다. 특히, 공통의 서비스나 변수를 공유할 때 유용합니다. 다음은 이러한 중첩 클래스를 테스트 케이스에 적용하는 예시입니다:
@ExtendWith(MockitoExtension.class)
class MyTestClass {
@Mock
private ServiceToCall service; // 테스트 대상 서비스가 호출할 서비스
@InjectMocks
private ServiceUnderTest sut; // 테스트하려는 서비스
@Nested
class ServiceDoSomething{
@BeforeEach
void setup(){
when(service.doSomething()).thenReturn("something");
}
@Test
void testMyService(){
//TODO 테스트 케이스를 구현하세요
}
}
@Nested
class ServiceDoesNothing{
@BeforeEach
void setup(){
when(service.doSomething()).thenReturn(null);
}
@Test
void testMyService(){
//TODO 테스트 케이스를 구현하세요
}
}
}
이 구현에서는 두 개의 중첩된 클래스가 있으며, 둘 다 service
와 테스트 클래스 sut
를 사용합니다. 하지만 각 클래스의 BeforeEach
설정이 다릅니다. 이 경우에는 덜 유용해 보일 수 있지만, 여러 테스트 케이스를 다룰 때 이 접근법은 매우 유용할 수 있습니다.
세번째 팁: 읽기 쉽고 깔끔한 Assertion 작성하기
개인적인 취향일 수 있지만, 저는 "org.assertj.core.api"의 assertThat
기능이 Assertion을 보다 가독성 있고 깔끔하게 만들어 준다고 생각합니다. 다음은 몇 가지 예시입니다.
assertThat(someList).hasSize(1);
assertNull(someText);
assertThat(someList).doesNotContain("idk");
assertThat(myText).isEqualTo("great");
assertThat(someList).isNotEmpty();
assertThat(someInfoList).isNotNull().hasSize(1)....
여러 개의 Assertion을 다룰 때는 assertAll
을 사용하는 것이 좋습니다. 이 방법은 여러 Assertion을 하나로 묶어 단일 테스트 케이스 내에서 여러 조건을 명확하고 체계적으로 확인할 수 있게 해줍니다.
assertAll(
() -> assertThat(text1).isEqualTo(text2),
() -> assertThat(text1).isEqualTo(text3),
...
);
네번째 팁: 매개변수화된 테스트 사용하기
때때로 여러 테스트 케이스를 각각 작성하는 것이 더 쉬워 보일 수 있습니다. 하지만 이럴 때는 입력의 한 가지 매개변수만 다르다면 매개변수화된 테스트를 사용하는 것이 좋습니다. 이와 관련해 이론보다 실용성을 중시하는 저는 JUnit 5를 사용한 예시를 보여드리겠습니다:
@ParameterizedTest
@ValueSource(ints = {1, 3, 5})
void testNumbers(int number) {
sut.doSomethingWithMyNumber(number);
}
또한, Enum과 CSV 형식의 데이터를 테스트 케이스에서 활용하는 것도 좋은 방법입니다. 다음 번 테스트 작성 시 이를 한번 시도해 보세요.
이 글이 여러분의 테스트 작성 실력을 향상시키는 데 도움이 되었기를 바랍니다. 하지만 테스트가 아무리 깔끔하고 아름답게 작성되었더라도, 복잡한 코드에서 단 몇 개의 테스트 케이스만 작성되었다면 그 효과는 제한적일 수 있습니다.
읽어주셔서 감사합니다! 이 글이 도움이 되셨다면 공감이나 댓글을 남겨주시면 감사하겠습니다. 여러분의 피드백은 큰 힘이 됩니다!
읽어주셔서 감사합니다! 😊
개발 관련 궁금증이나 고민이 있으신가요?
아래 링크를 통해 저에게 바로 문의해 주세요! 쉽고 빠르게 도움 드리겠습니다.
'Development > Code' 카테고리의 다른 글
[스프링 부트] 지금 당장 적용해야 할 25가지 Spring Boot 모범 사례 (0) | 2024.09.02 |
---|---|
[자바] 개발자 필독! 지금 바로 적용할 수 있는 Java 꿀팁 30가지! (2) | 2024.08.27 |
Java 22 살펴보기: 외부 함수 및 메모리 API (0) | 2024.08.09 |
React 전문가들이 추천하는 15가지 라이브러리 (1) | 2024.08.06 |
Hibernate 6 로 마이그레이션: 새로운 기능과 변경된 사항 (0) | 2024.08.05 |