Junit 5 테스트 코드 작성 가이드
해당 문서는 Junit 5 테스트 코드 작성 가이드에 관한 문서입니다. Junit 4와 Junit 5의 차이점을 알아보고 Junit5 유저 가이드를 공부하며 제대로 테스트코드를 작성하기 위해 만들어진 문서입니다.
Junit 5 특징
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
Junit4가 단일 jar였던 것에 반해, Junit5는 Junit Platform, Junit Jupiter, Junit Vintage 모듈 세 가지로 구성되어 있습니다.
Junit Platform
- JVM에서 동작하는 테스트 프레임워크입니다. 테스트를 발견하고 계획을 생성하고 결과를 보고하는 TestEngine 인터페이스를 정의합니다.
Junit Jupiter
- Junit5 TestEngine의 실제 구현체입니다. Junit5 기반의 테스트를 실행시키기 위한 TestEngine을 Platform에 제공합니다.
Junit Vintage
- TestEngine에서 Junit3 및 Junit4 기반 테스트를 실행하기 위한 기능을 제공합니다.
Junit5는 Java 8 이상의 버전을 요구합니다.
Junit 5 기본 테스트 케이스 형식 예시
import static org.junit.jupiter.api.Assertions.assertEquals; import example.util.Calculator; import org.junit.jupiter.api.Test; class MyFirstJUnitJupiterTests { private final Calculator calculator = new Calculator(); @Test void addition() { assertEquals(2, calculator.add(1, 1)); } }
Junit 5 Annotation
Junit5에서 제공하는 org.junit.jupiter.api 패키지 내의 어노테이션을 설명합니다.
-
@Test
- 해당 어노테이션을 달아둔 메서드가 테스트 메서드임을 나타냅니다.
-
@BeforeEach
- 각각의 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 전에 실행됩니다.
-
@AfterEach
- 각각의 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 후에 실행됩니다.
-
@BeforeAll
- 모든 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 전에 실행됩니다.
-
@AfterAll
- 모든 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 후에 실행됩니다.
-
@ExtendWith
- 확장을 선언적으로 등록하는데 사용됩니다. Extendtion 뒤에 인자로 확장할 Extension을 추가하여 사용합니다.
- Spring을 사용할 경우 @ExtendWith(SpringExtension.class)와 같이 사용합니다.
-
@Disabled
- 테스트 클래스 또는 테스트 메서드를 비활성화 하는데 사용됩니다.
-
@ParameterizedTest
- 메서드가 매개변수가 있는 테스트임을 나타냅니다.
-
@RepeatedTest
- 메서드가 반복 테스트를 위한 테스트 템플릿임을 나타냅니다.
-
@TestFactory
- 메서드가 동적 테스트를 위한 테스트 팩토리임을 나타냅니다.
-
@TestMethodOrder
- 테스트 메서드 실행 순서를 구성하는데 사용됩니다.
-
@DisplayName
- 테스트 클래스 또는 테스트 메서드에 대한 사용자 지정 표시 이름을 정해줄 때 사용됩니다.
Junit 4 vs Junit 5 (Migration)
Junit5와 Junit4 는 일무 어노테이션에서도 차이가 있습니다. Junit5로 넘어가며 어떤 것이 차이가 있는지 알아보려 합니다.
이전에 Junit 4 기반으로 작성된 파일들은 위에서 설명했던 것과 같이 junit-vintage-engine이 있다면 자동으로 Junit Platform 런처에서 Junit 3, 4기반 테스트 코드를 선택합니다. 이미 만들어진 Junit 4 파일을 5버전으로 수정할 필요는 없습니다.
Assertion
의 위치가 Junit5에서는org.junit.jupiter.api.Assertions
으로 변경되었습니다. AssertJ, Hamcrest, Trust에서 제공하는 org.junit.Assert는 그대로 사용할 수 있습니다.@Before
과@After
어노테이션이 사라지고, 각각@BeforeEach
와@AfterEach
로 변경되었습니다.@BeforeClass
와@AfterClass
어노테이션이 사라지고,@BeforeAll
과@AfterAll
로 변경되었습니다.@Ignore
이 사라지고,@Disabled
로 변경되었습니다.- 단, 이 경우
@EnableJUnit4MigrationSupport
어노테이션을 붙여줍니다.
- 단, 이 경우
@Category
가 사라지고,@Tag
로 변경되었습니다.@RunWith
이 사라지고@ExtendWith
으로 변경되었습니다.@Rule
과@ClassRule
이 사라지고,@ExtendWith
과@RegisterExtention
으로 대체되었습니다.
Test에 순서 추가하기
@TestMethodOrder(OrderAnnotation.class)
@Order
Junit5 설정하기
-
Junit5 의존성 추가하기 (gradle)
dependencies { testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.7.0") testImplementation('org.junit.jupiter:junit-jupiter:5.7.0') testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.7.0") } Junit4에 대한 의존성이 필요하지 않다면, junit-vintage-engine 종속성은 제외하고 추가합니다. 만약 spring-boot-starter-test를 사용한다면, 아래와 같이 vintage를 제외합니다.
testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } -
test파일에 jupiter 추가해주고 assert 사용하려면 static으로 import 해주기
import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; junit에서 제공하는 Assertion말고 AssertJ를 사용할 경우 아래와 같이 정의해줍니다.
import org.junit.jupiter.api.*; import static org.assertj.core.api.Assertions.*;
Mapper Test Code 예시
... import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.*; @ExtendWith(SpringExtension.class) //Junit4의 Runwith과 같은 기능을 하는 Junit5 어노테이션 @SpringBootTest(classes = PmcwebApplication.class) // Junit5 기준 Application Context사용할 때 사용 @TestMethodOrder(MethodOrderer.OrderAnnotation.class) // Order를 붙일 때 사용 @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 진짜 데이터베이스에 테스트 public class UserMapperTest { @Autowired private AccountMapper userMapper; private static final String testEmail = "test@naver.com"; private static final String testName = "test"; @Test @Order(1) void createUser() { Account account = Account.builder() .name(testName) .email(testEmail) .password("1234") .status(UserStatus.REGISTERED.getTitle()) .role(UserRole.NORMAL.getTitle()) .instTime(new Date(System.currentTimeMillis())) .build(); userMapper.createUser(account); Account createdUser = userMapper.getUserByEmail(testEmail); assertThat(createdUser.getEmail().equals(account.getEmail())); } @Test @Order(2) void getUserByEmail() { Account getUser = userMapper.getUserByEmail(testEmail); assertThat(testName.equals(getUser.getName())); } @Test @Order(3) void getUserList() { List<Account> userList = userMapper.getUserList(); assertThat(userList.size() > 0); } @Test @Order(4) void deleteUser() { Account getUser = userMapper.getUserByEmail(testEmail); assertThat(getUser!=null); userMapper.deleteUser(getUser.getId()); assertThat(userMapper.getUserById(getUser.getId())==null); } }
이후 Optional로 변경하면, get하는 곳이 변경될 예정입니다.
Service Test Code 예시
... import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.*; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = PmcwebApplication.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class UserServiceTest { @Autowired private AccountServiceImpl userServiceImpl; private static final String testEmail = "test@naver.com"; private static final String testName = "test"; @Test @Order(1) void createUser() { Account account = Account.builder() .name(testName) .email(testEmail) .password("1234") .status(UserStatus.REGISTERED.getTitle()) .role(UserRole.NORMAL.getTitle()) .instTime(new Date(System.currentTimeMillis())) .build(); userServiceImpl.createUser(account); Account findUser = userServiceImpl.getUserByEmail(account.getEmail()); assertThat(account.getEmail()).isEqualTo(findUser.getEmail()); } @Test @Order(2) void getUser() { Account getUser = userServiceImpl.getUserByEmail(testEmail); assertThat(testEmail.equals(getUser.getEmail())); assertThat(testName.equals(getUser.getName())); } @Test @Order(3) void getUsers() { List<Account> userList = userServiceImpl.getUsers(); assertThat(userList.size() > 0); } @Test @Order(4) void deleteUser() { Account getUser = userServiceImpl.getUserByEmail(testEmail); assertThat(userServiceImpl.getUserByEmail(testEmail)!=null); userServiceImpl.deleteUser(getUser.getId()); assertThat(userServiceImpl.getUserByEmail(testEmail)==null); }
Controller Test Code 예시
... import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; @ExtendWith(SpringExtension.class) @WebMvcTest(AccountController.class) class AccountControllerTest { @Autowired private MockMvc mockMvc; @MockBean private AccountService accountService; @MockBean private AccountSecurityService accountSecurityService; @Test public void getSignup() throws Exception { this.mockMvc.perform(get("/user/signup")) .andDo(print()) .andExpect(result -> "user/register".equals(result)) .andExpect(status().isOk()); } ...
UserController 테스트 코드는 아직 완성되지 않았다. 일단 시큐리티와 연동되지 않은 것들은 위와 같은 방식으로 테스트가 가능하다.
현재 로그인한 유저 정보(@AuthenticationPrincipal 로 받아온 유저)를 얻어와서 테스트를 해야 하는데 MockUser를 얻어오는 방법을 모르겠다. 그냥 static하게 WithMockUser로 항상 생성되어 있는 유저로 사용해야 하나?
다음에는 시큐리티와 연동하여 Mock 테스트를 하는 것을 알아보자.
참고 자료
- Junit 5 유저 가이드
- Junit 4 -> Junit 5로 마이그레이션 하기
'개발 > Spring' 카테고리의 다른 글
[Spring 프로젝트] AOP Logging (Post 메서드의 Json Body 값 로깅하기) (0) | 2020.12.04 |
---|---|
[Spring 프로젝트] Mybatis에서 Insert, Update Batch 처리하기 (0) | 2020.12.02 |
[Spring] 컴포넌트 스캔 (0) | 2020.11.17 |
[Spring] 스프링 의존 자동 주입 (Auto Injection)이란? (0) | 2020.11.16 |
[Spring 프로젝트] 2. ERD 설계하기 (0) | 2020.11.16 |