Junit4에서 Junit5로 전환하기
Junit5는 새로운 아키텍처를 적용한 새로운 패러다임으로 Junit Vintage 테스트 엔진을 활용하여 Junit4에서 Junit5로 전환할 수 있다.
Junit Jupiter와 관련한 모든 클래스와 애노테이션은 org.junit.jupiter패키지에서 확인할 수 있고, org.junit 패키지에 Junit4와 관련한 모든 클래스와 애노테이션이 있으므로 클래스 패스에 Junit 5 Jupiter와 Junit4가 모두 존재하더라도 충돌이 발생하지 않는다. ( 예를 들면, Junit4의 @Test 애노테이션은 org.junit.Test에 속해 있고 Junit5의 @Test 애노테이션은 org.junit.jupiter.api.Test에 속해 있어 충돌이 발생하지 않고 각자 불러올 수 있다. )
Junit4에서 Junit5로의 전환은 다음과 같은 단계를 거친다. 전환 시 Junit4는 java5 이상을, Junit5는 java8 이상을 요구하니 설치된 자바 버전을 확인하는 것이 좋다.
- 의존성 교체
Junit4를 Junit5으로 전환시 junit-vintage-engine 의존성을 추가해야 한다. 의존성 추가 방법은 다음과 같다. ( 보다 자세한 설정은 해당 링크 참조 ) 해당 의존성을 추가하면 Junit4에 대한 의존성 파일을 추가하지 않아도 정상적으로 Junit4가 포함된 테스트 코드를 실행시킬 수 있다.
dependencies {
testImplementation("junit:junit:4.13.2")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.10.2")
}
- Junit4 애노테이션을 Junit5로 애노테이션으로 교체
- 테스트 클래스와 메서드를 교체
Jinit5를 사용하여 테스트를 작성하기 위해 junit-jupiter 의존성을 추가 해줘야 한다. jupiter에는 애노테이션, 클래스, 메서드가 포함된 junit-jupiter-api와 테스트 엔진을 실행하기 위한 junit-jupiter-engine가 포함되어 있다. 이 외에도 파라미터를 사용한 테스트 작성이 필요하다면 junit-jupiter-params가 추가적으로 필요할 수 있다.
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
}
Junit4와 Junit5에서 유사하게 사용되는 애노테이션,클래스,메서드는 다음과 같다.
- 애노테이션
Junit4 | Junit5 |
@BeforeClass, @AfterClass | @BeforeAll, @AfterAll |
@Before, @After | @BeforeEach, @AfterEach |
@Ignore | @Disabled |
@Category | @Tag |
@BeforeClass, @AfterClass는 static이어야 하고 public이어야 했던 점이 Junit5에서 static이 유지되는 것은 똑같지만 접근제어 수준이 default로 완화되었다. 또한 @TestInstance(Lifecyle.PER_CLASS)을 사용하여 static이 아니라 비정적(none-static)으로 선언할 수 있게 되었다.
@Category가 아닌 @Tag를 사용하면 코드 수준에서 테스트 수준을 묶을 수 있다. 간단한 예시를 보자. 아래 코드를 작성하여 각각 IndividualTests와 RepositoryTest를 정의할 수 있다.
// @Category로 묶을 interface 선언
public interface IndividualTests {
}
public interface RepositoryTests {
}
public class JUnit4CustomerTest {
private String CUSTOMER_NAME = "John Smith";
@Category(IndividualTests.class) // 테스트를 각각 특정 카테고리에 매핑시킴
@Test
public void testCustomer() {
// test code
}
@Category({IndividualTests.class, RepositoryTests.class}) // 테스트를 각각 특정 카테고리에 매핑시킴
@Test
public void testCustomer() {
// test code
}
}
@Category({IndividualTests.class, RepositoryTests.class}) // 클래스 수준에서 애노테이션을 추가하여 클래스 안에 있는 테스트 메서드 모두를 특정 카테고리에 포함되게 만들 수 있음
public class JUnit4CustomerTest {
private String CUSTOMER_NAME = "John Smith";
@Test
public void testCustomer() {
// test code
}
@Test
public void testCustomer() {
// test code
}
}
/**
*Junit4CustomerTest.class, Junit5CustomerRepositoryTest.class 클래스에서
*IndividualTest 애노테이션이 포함된 테스트만 실행 시키도록함
**/
@RunWith(Categories.calss)
@Categories.IncludeCategory(IndividualTests.class) // @Categories.ExcludeCategory(x.class)를 사용하면 제외할 목록을 지정할 수 있음
@Suite.SuiteClass({Junit4CustomerTest.class, Junit4CustomerRepositoryTest.class}) // 테스트 하고자 하는 클래스들 나열
public class Junit4IndividualTestsSuite {
}
Junit4의 @Category는 테스트 묶음을 위한 인터페이스를 만들어야 하고, @Category의 파라미터에 들어갈 마커 인터페이스를 전부 따로 작성해야 하는 번거로움을 가진다. Junit5에서는 이러한 번거로움이 사라졌다. 단순히 @Tag("구분할 명칭")을 클래스나 테스트 단위로 지정하고, IDE를 사용하여 실행할 태그를 선택하면 끝이다.
- 단언문
Junit4 | Junit5 |
Assert 클래스를 사용한다 | Assertions 클래스를 사용한다 |
단언문 메시지는 첫 번째 파라미터에 적는다 | 단언문 메시지는 마지막 파라미터에 적는다 |
assertThat 메서드를 사용할 수 있다. | assertThat 메서드를 지원하지 않는다. assertAll과 assertThrows 메서드가 추가 되었다. |
- 단언문에서 assertThat의 지원이 종료되고, Hamcrest패키지로 옮겨가게 되었다. import 하는 패키지만 달라졌을 뿐이지 문법은 동일하다. 예를들면 이런식이다.
// Junit4 - org.junit.Assert.assertThat
assertThat(values, hasItem(anyOf(equalTo("Oliver"), eqaulTo("Jack"), eqaulTo("Harry"))));
//Junit5 - org.hamcrest.Matcher.Assert.assertThat
assertThat(values, hasItem(anyOf(equalTo("Oliver"), equalTo("Jack"), equalTo("Harry"))));
- 가정문
Junit4 | Junit5 |
Assume 클래스를 사용한다 | Assumptions 클래스를 사용한다 |
assumeNotNull, assumeNoException 메서드를 사용할 수 있다 | assumeNotNull, assumeNoException 메서드를 사용할 수 없다 |
- Junit4 rule과 runner를 Junit5의 확장모델로 교체
cf. Junit4 rule
* Junit4 rule 이란?
메서드가 실행될 때 호출을 가로채고 메서드 실행 전후에 추가 작업을 수행할 수 있는 Junit4 컴포넌트다. ExpectedException, TemporaryFolder 혹은 CustomRule을 직접 작성하여 사용할 수 있다.
Junit4 rule에서 ExpectedException은 Junit5에서 assertThrows 메서드로 쉽게 대체할 수 있다. 간단한 예제를 보자.
// Junit4
public class JUnit5RuleExceptionTester {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void expectIllegalArgumentException() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("blarblar...");
//에러 예상 코드 작성
}
}
// Junit5
public class JUnit5RuleExceptionTester {
@Test
public void expectIllegalArgumentException() {
Throwable throwable = assertThorws(IllegalArgumentException.class,
() -> caculator.sqrt(-1)); // 에러 예상 코드 작성
assertEquals("blarblar...", throwable.getMessage());
}
}
이 외에도, Junit4에서 @Rule을 사용해서 TemporaryFolder 타입을 삽입하는 대신 Junit5의 @TempDir을 사용하여 간편하게 사용하는 것도 가능하다. 테스트 실행 전후로 비슷한 작업이 필요한 경우네는 사용자 정의 rule을 사용할 수 있다.
다음은 TestRule을 implements 하여 CustomRule을 작성하는 예제이다.
//Junit4 에서 TestRule을 참조하는 CustomRule 작성 예제
public class CustomRule implements TestRule {
private Statement base;
private Descrption description;
@Override
public Statement apply(Statement base, Description description) {
this.base = base;
this.description = description;
return new CustomStatement(base, descrption);
}
}
혹은 Satement를 impements 하는 CustomRule 클래스를 생성할 수도 있다. 생성된 CustomRule을 실행시키기 위해서 테스트 클래스 객체를 별도로 만들어 실행시킬 수 있다.
public class Junit4CustomRuleTest {
@Rule
public CustomRule myRule = new CustomRule();
@Test
public void myCustomRuleTest() {
System.out.printlnt("call of a test method");
}
}
Junit5에서는 사용자 정의 extension으로 테스트 클래스와 테스트 메서드의 동작을 확장하고 개발자들이 Junit4 rule과 유사한 효과를 누릴 수 있도록 했다. Junit5의 사용자 정의 extension을 사용하게 되면 코드가 짧아지고, 애노테이션을 사용할 수 있어 소스 코드가 선언적으로 바뀐다.
//Junit5에서 Junit4의 @Rule을 대체하여 @ExtendWith을 사용하는 예제
public class CustomExtension implements AfterEachCallback, BeforeEachCallback {
// AfterEachCallback, BeforeEachCallback 인터페이스를 구현하여 @BeforeEach와 @AfterEach 처럼 동작한다
@Override
public void beforeEach(ExtensionContext extensionContext) throws Exception {
System.out.println(this.getClass().getSimpleName() + extensionContext.getDisplayName() );
}
@Override
public void afterEach(ExtensionContext extensionContext) throws Exception {
System.out.printlnt(this.getClass().getSimpleName() + extensionContext.getDisplayName());
}
}
@ExtendWith(CustomExtension.class)
public class Junit5CustomExtensionTester {
@Test
public void myCustomRuleTest() {
System.out.println("Call of a test method");
}
}
Junit5 Extension을 사용하여 Junit4의 runner을 점차적으로 대체할 수도 있다. 가령, Mockito 테스트 전환을 위해서는 @RunWith(MockitoJunitRunner.class) 애노테이션을 @ExtendWith(MockitoExtension.class)로 바꾸면 된다. 스프링 테스트는 @RunWith(SpringJUnit4 ClassRunner.class) 애노테이션을 @ExtendWith(SpringExtension.class)로 대체할 수 있다.