JUnit 테스트 코드를 작성해보자.
예전에 진행했던 SNS 프로젝트를 다시 Rest API로 구현하는 중에 JUnit5로 테스트 코드를 작성해보기로 했다. 아직 구현 기능이 많지 않고 이번 프로젝트에서 DB, 서버 배포 등을 구체적으로 결정하지 않았기 때문에 천천히 예제로 테스트 코드를 작성해보고 프로젝트에도 적용할 예정이다.
JUnit5 특징
JUnit4가 단일 jar 였던 것에 반해 JUnit5는 다음과 같은 모듈로 구성되어 있다.
- JUnit Platform
JVM에서 동작하는 테스트 프레임워크
테스트를 발견하고 계획을 생성하고 결과를 보고하는 TestEngine 인터페이스를 정의
- JUnit Jupiter
TestEngine의 실제 구현체, 테스트를 실행시키기 위해 TestEngine을 Platform에 제공
- JUnit Vintage TestEngine에서 JUnit3 및 JUnit4 기반 테스트를 실행하기 위한 기능을 제공
Gradle 설정
dependencies {
. . .
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude module: 'junit'
}
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}
test {
useJUnitPlatform()
}
위 JUnit5의 특징에서 알아봤듯이 junit에 3, 4 버전이 포함되어 있기 때문에
junit5만 사용할 생각이라면 위 gradle 설정처럼 junit을 제외하면 된다.
그러나 junit5는 jupiter-api, jupiter-engine을 필요로하기 때문에 위와 같은 설정을 추가해야 한다.
Spring Boot에서는 어떻게?
스프링 부트 2.0에서 일반적인 스프링과 마찬가지로 @ExtendWith
어노테이션을 사용하지만
@ContextConfiguration
대신에 @SpringBootTest
을 사용한다.
@SpringBootTest
어노테이션은 애플리케이션 컨텍스트를 실행해준다.
@ExtendWith(SpringExtension.class)
@SpringBootTest
class SimpleSpringTest {
@Autowired
public MessageComponent messageComponent;
@Test
public void test() {
assertEquals("Hello world!", messageComponent.getMessage());
}
}
Spring Boot가 아니라면?
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { MySpringApplication.class })
class SimpleSpringTest {
. . .
}
@ExtendWith
: spring 또는 Mokito 라이브러리를 확장해서 사용 가능@ContextConfiguration
: 이 어노테이션으로 컨텍스트 설정 가능
JUnit5와 Mockito 사용
Mockito는 개발자가 동작을 직접 제어할 수 있는 가짜(Mock) 객체를 지원하는 테스트 프레임워크다.
Spring은 여러 객체들 간의 의존성이 존재해 단위 테스트 작성이 어려워질 수 있는데
이를 해결하기 위해 가짜 객체를 주입시키는 Mockito 라이브러리를 활용할 수 있다.
1) Mock 객체 의존성 주입
Mockito에서 Mock 객체의 의존성 주입을 위해 어노테이션을 사용한다.
@Mock
: Mock 객체를 만들어 반환해주는 어노테이션@Spy
: Stub하지 않은 메소드들은 원본 메소드 그대로 사용하는 어노테이션@InjectMocks
: 가짜 객체를 자동으로 주입시켜주는 어노테이션
예를들어 UserController에 대한 단위 테스트를 작성하고자 할 때, UserService를 사용하고 있다면
@Mock
어노테이션으로 가짜 UserService를 만들고,
@InjectMocks
어노테이션으로 UserController에 이를 주입시킬 수 있다.
2) Stub
의존성이 있는 객체는 가짜 객체를 주입하면 어떤 결과를 반환하라고 정해진 답변을 준비시켜야 한다.
Mockito는 다음과 같은 stub 메서드를 제공한다.
doReturn()
: Mock 객체가 특정한 값을 반환해야 하는 경우doNothing()
: Mock 객체가 아무것도 반환하지 않는 경우doThrow()
: Mock 객체가 예외를 발생시키는 경우
예를들어 UserService의 findAllUser() 호출 시 빈 ArrayList를 반환해야 한다면 다음과 같이 작성하면 된다.
doReturn(new ArrayList()).when(userService).findAllUser();
3) Mockito와 JUnit의 결합
Mockito도 테스팅 프레임워크다. 그래서 JUnit과 결합하기 위해 별도의 작업이 필요하다.
기존의 JUnit4에서 Mockito를 사용하기 위해 클래스 어노테이션으로
@RunWith(MockitoJUnitRunner.class)
를 불여야했다.
JUnit5부터는 @ExtendWith(MockitoExtention.class)
를 사용해야 결합이 가능하다.
참고
- JUnit에 대한 블로그 게시글 https://hyerin6.github.io/2020-01-13/Junit-테스트-구현/
- 공식 레퍼런스 https://junit.org/junit5/docs/current/user-guide/#overview
- 샘플 코드 https://github.com/junit-team/junit5-samples/tree/main/junit5-jupiter-starter-gradle
- Mockito https://mangkyu.tistory.com/145