과제 0. JUnit 5 학습하세요.
-
인텔리J, 이클립스, VS Code에서 JUnit 5로 테스트 코드 작성하는 방법에 익숙해 질 것.
-
이미 JUnit 알고 계신분들은 다른 것 아무거나!
-
더 자바, 테스트 강의도 있으니 참고하세요~
과제 1. live-study 대시 보드를 만드는 코드를 작성하세요.(진행중)
-
깃헙 이슈 1번부터 18번까지 댓글을 순회하며 댓글을 남긴 사용자를 체크 할 것.
-
참여율을 계산하세요. 총 18회에 중에 몇 %를 참여했는지 소숫점 두자리가지 보여줄 것.
-
Github 자바 라이브러리를 사용하면 편리합니다.
-
깃헙 API를 익명으로 호출하는데 제한이 있기 때문에 본인의 깃헙 프로젝트에 이슈를 만들고 테스트를 하시면 더 자주 테스트할 수 있습니다.
JUNIT
자바의 단위테스트 도구이다. 외부 테스트 프로그램을 작성하여 System.out으로 번거롭게 디버깅 하지 않아도 되며 ,
프로그램 테스트 시 걸릴 시간도 관리할 수 있게 해주는 오픈소스이다. 어느정도 개발이 진행되면 프로그램에 대한 단위 테스트는 반드시 수행해야한다. Junit은 보이지 않고 숨겨진 단위 테스트를 끌어내 정형화시켜 단위테스트를 쉽게 해준다.
단위테스트(Unit Test)
- 소스 코드의 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차
- 모든 함수와 메소드에 대한 테스트 케이스를 작성하는 절차를 말한다.
JUnit의 특징
-@Test 메서드가 호출 될때 마다 새로운 인스턴스를 생성하여 독립적인 테스트가 이루어지게 한다.
-assert메서드로 테스트 케이스의 수행 결과를 판별할 수있다.
-JUnit4 부터는 어노테이션으로 간결하게 테스트를 지원한다. (@Test @Befor @After)
-테스트의 결과가 성공일경우 (녹색) , 실패시(붉은색) 중 하나로 표시된다.
-테스트 결과를 확인하는 것 이외 최적화된 코드를 유추해내는 기능도 제공한다.
JUNIT 5
JUnit 5 -> Junit Platform + JUnit Jupter + Junit Vintage
JUnit Platform : 테스트를 발견하고 테스트 계획을 생성하는 TestEngine 인터페이스를 가지고 있습니다. Platform은 TestEngine을 통해서 테스트를 발경하고 ,실행하고 ,결과를 보고한다.
JUnit Jupiter :TestEngine의 실제 구현체는 별도의 모듈이며 . 모듈 중 하나가 jupiter-engine이다. 이 모듈은 jupiter-API를 사용하여 작성한 테스트 코드를 발견하고 실행한다 .
Jupiter API는 JUnit 5에 새롭게 추가된 테스트 코드용 API로 , 개발자는 Jupiter API를 사용해서 테스트 코드를 작성할 수 있다.
JUnit Vintage : 기존에 JUnit 4 버전으로 작성한 테스트 코드를 실행할 때에는 vintage-engine 모듈을 사용한다.
Junit5는 런타임시 Java 8버전 이상을 필요로한다 . 하지만 이전 버전의 JDK로 컴파일된 코드는 테스트 가능하다.
아래부터는
junit.org/junit5/docs/current/user-guide/ 의 내용을 번역 하였습니다. 구글 번역기와 제 머리를 함께 사용하기 때문에 틀리거나 어색한 부분이 있을 수 있습니다.
사용법
maven dependency
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
Gradle
testCompile("org.junit.jupiter:junit-jupiter-params:5.7.0")
위 dependency를 추가하여 사용하면 된다.
1. 테스트 작성
아래의 예제는 JUnit Jupiter에서 테스트를 작성하기 위한 최소 요구 사항을 간략하게 보여준다
이 장의 이후 섹션에서는 사용 가능한 모든 기능에 대한 자세한 내용을 제공하고 있다.
public class Calculator {
public int add(int a , int b){
return a+b;
}
public int minus(int a, int b){
return a-b;
}
public int multiple(int a, int b){
return a*b;
}
public int division(int a , int b){
return a/b;
}
}
테스트코드 작성을 위한 간단한 계산기 클래스이다.
package JUnit5;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class CalculatorTest {
private final Calculator calculator = new Calculator();
@Test
void addition(){
Assertions.assertEquals(2,calculator.add(1,1));
}
}
내가 만든 Calculator 클래스를 검증하는 Test 이다. Test code는 java폴더가 아닌 test 폴더에 저장해놔야한다.
@Test 어노테이션은 해당 메서드는 Test 코드라는 것을 지정한다,
assert메서드는 Assertions 클래스에 static메서드로 구현되 있다.
테스트 방식은 간단하다 . assert메서드를 활용해 예상하는 값을 지정하고 실제 테스트하는 메서드의 값을 비교하여 테스트한다.
위의 Test는 calculator의 add 메서드를 검증하고 있다.
예상값으로 2를 주었고 , 다음 인자로는 add 메서드를 사용하여 리턴되는 값을 지정하였다.
해당 테스트를 실행하면 아래와 같이 초록색 불이 뜬다.
실패할 경우 아래와 같이 예상값과 실제값,실패 이유 , error 메시지 등이 표시 된다.
2.어노테이션
JUnit 테스트 구성 및 확장을 위한 어노테이션 들을 정리해 보았다. 해당 어노테이션은 junit-jupiter-api 모듈에 org.junit.jupiter.api패키지에 위치한다.
어노테이션 | 설명 |
@Test | 메서드가 테스트 메서드임을 나타낸다. JUnit4의 @Test 어노테이션과 달리 JUnit Jupiter의 테스트 확장이 자체 전용 어노테이션 기반으로 작동하기 때문에 속성을 선언하지 않아도 된다. |
@ParameteriedTest | 매개 변수화 된 테스트임을 나타낸다 . 해당 어노테이션을 포함한 메서드는 재정의 되지 않는 한 상속된다. JUnit 5.7.0버전 이상부터 사용가능하다 여러개의 argument를 이용해 테스트를 한번에 여러개의 테스트를 돌릴 수 있으며 , @ValuesSource (이외에도 여러가지 타입의 source를 사용할 수 있다. 여러가지 타입 source 어노테이션 (lannstark.tistory.com/52 )을 참조하자.) 같은 source 어노테이션을 이용해 값을 지정해 줄 수 있다. |
@RepeatedTest | 동일한 테스트를 반복해서 수행해야할 경우 사용하는 어노테이션입니다. 성능적인 이슈를 확인하거나 하는 반복적인 테스트를 수행할 경우에 사용할 수 있다. @RepeatedTest(10) 과 같이 반복 횟수를 정하여 테스트를 반복 실행 시킬 수 있다. |
@TestFactory | 동적 테스트를 위한 test factory 임을 나타내는 어노테이션. 일반적으로 JUnit의 단위 테스트는 @Test 어노테이션을 작성한 테스트 메소드에 기술하는 것으로 적용되지만. 이를 사용하지 않고 동적으로 테스트를 작성할 수 있게 해주는 어노테이션이다 . |
@TestTemplate | @Test 메소드와 달리 테스트 템플릿은 그자체카 테스트 케이스가 아니라 테스트 케이스의 템플릿이다. 따라서 등록된 공급자가 반환하는 호출 컨텍스트 수에 따라 여러 번 호출되도록 설계되었으며 하나 이상의 공급자와 함께 사용해야 한다. |
@TestMethodOrder | 테스트 메서드 실행 순서를 구성하는데 사용된다 . JUnit4의 @FixMethodOrder와 유사하다. 일반적으로 테스트는 순서에 의존하지 않도록 작성하는 것이 유지보수 측면에서 바람직하다. 하지만 시퀀셜한 업무흐름을 순서대로 테스트하는 것이 테스트 코드 작성에 편할 경우에 사용할 수 있다. |
@TestInstance | 해당 어노테이션이 붙은 테스트 인스턴스의 라이프사이클을 설정하는데에 사용된다. |
@DisplayName | 테스트 클래스 혹은 테스트 메서드의 이름을 지정하는데에 사용된다. |
@DisplayNameGeneration | JUnit5에서 새로 추가된 어노테이션이며 카멜케이스,언더스코어제거등의 클래스를 상속받아 만든 Generator를 지정하여 test class name을 genaration할 수 있다. @DisplayName 보다 우선순위가 낮다. |
@BeforeEach | JUnit4의 @Before와 같은 역할을하며 @BeforeEach가 달린 메서드는 클래스에 위치한 모든 @Test @RepeatedTest @ParameterizedTest @TestFactory 테스트 이전에 실행된다. |
@AfterEach | JUnit4의 @After와 같은 역할을하며 @AfterEach가 달린 메서드는 클래스에 위치한 모든 @Test @RepeatedTest @ParameterizedTest @TestFactory 테스트 이후에 실행된다. |
@BeforeAll | 클래스 내의 모든 메서드 실행 전에 한번 실행되며 @BeforeAll 메서드는 static으로 선언되 있어야 한다. |
@AfterAll | 클래스 내의 모든 메서드 실행 후에 한번 실행되며 @AfterAll 메서드는 static으로 선언되 있어야 한다. |
@Nested | 클래스 내의 중첩클래스를 선언할 때 사용하는 어노테이션이며 @Nested가 달린 클래스는 @BeforeAll , @AfterAll의 사용이 불가능 하다 .(중첩 클래스에서 static선언이 불가한 것 처럼) |
@Tag | 클래스 혹은 메서드 레벨에서 필터링을 위해 사용되는 Tag를 지정하는 어노테이션이다 CI 빌드 테스트 자동 실행시에 필터링을 통해 특정 태그 이름으로 테스트를 개별적으로 실행할 수 있다. |
@Disabled | 테스트 클래스 혹은 메서드를 비활성화 시키기 위해 사용한다, Junit4의 @Ignore와 같다 |
@Timeout | 주어진 시간을 초과하는 경우 테스트 , 테스트 팩토리 ,테스트 템플릿 또는 라이프사이클 메서드를 실패하는데 사용된다. |
@ExtendWith | 단위 테스트간에 공통적으로 사용할 기능을 구현하여 @ExtendWith를 통하여 적용할 수 있는 기능을 제공합니다. 확장기능은 org.junit.jupiter.api.extension.Extension 인터페이스를 상속한 인터페이스로 되어있으며 JUnit5에서 제공하는 기능의 상당수가 이 기능을 통해서 지원되고 있다 |
@RegisterExtension | @ExtendWith 어노테이션을 통해서 확장기능을 선언적으로 등록할 수 있다라고 한다면 @RegisterExtension을 통해서는 절차적 즉, 프로그램 코드를 이용하여 확장기능을 등록할 수 있습니다. @ExtendWith로 충분하다고 생각되므로 크게 유용성을 못느낄수도 있지만 있겠지만 어노테이션 기반으로 사용하는 경우와는 다르게 생성자(Constructor)를 통해 확장기능에 의존성을 주입하거나, 빌더등을 통해서 프로그램을 통한 설정이 가능해집니다. |
@TempDir | 라이프 사이클 메서드 또는 테스트 메서드에서 필드주입 또는 매개 변수 주입을 통해 임시 디렉토리를 제공하는 데 사용된다. org.junit.jupiter.api.io 패키지에 위치해 있다. |
3.Aseertions
JUnit jupiter는 JUnit4가 가지고 있는 많은 assertion 메소드와 함께 제공되며 java 8 람다와 함께 사용하기에 적합한 몇 가지가 추가 되었다. 모든 JUnit Jupiter의 assertion은 org.junit.jupiter.api.Assertions의 스태틱 메서드이다.
public class AssertionTest {
private final Calculator calculator = new Calculator();
private final Person person = new Person("ugo","hwang");
@Test
void standardAssertions(){
assertEquals(2,calculator.add(1,1));
//두번째 인자로 실패 메세지를 지정할 수 있다.
assertEquals(4,calculator.multiple(2,2),"틀렸다");
assertTrue('a'<'b',()->"틀렸다");
}
//그룹 Assertion 테스트
@Test
void groupedAssertions(){
//그룹안에 하나만 실패해도 해당 메서드는 실패하게 되고
//그룹화 된 Assertion에서는 모든 assertion이 실행되며 모든 실패는 함께 보고된다.
assertAll("person",
()->assertEquals("ugo",person.getFirstname()),
()->assertEquals("hwag",person.getLastname())
);
}
//코드 블록 내에 어설 션이 실패하면 동일한 블록의 후속코드는 건너뛴다.
@Test
void dependentAssertions(){
assertAll("properties",
() -> {
String firstName = person.getFirstname();
//null인지 테스트하는 Assertion
//여기서 Test가 실패하면 아래의 테스트는 모두 실행되지 않는다.
assertNotNull(firstName);
//여러개의 테스트를 한번에 구현하며 assertAll 안에 테스트중 하나만 실패해도 모두 실패한다.
assertAll("first name",
() -> assertTrue(firstName.startsWith("u")),
() -> assertTrue(firstName.endsWith("o"))
);
},
()-> {
String lastName = person.getLastname();
assertNotNull(lastName);
assertAll("last name",
() -> assertTrue(lastName.startsWith("h")),
() -> assertTrue(lastName.endsWith("g"))
);
});
}
@Test
void exceptionTesting(){
//발생할 예외에 대해서 test하고 예상하는 예외가 맞다면 Exceoption 변수에 담긴다
Exception exception = assertThrows(ArithmeticException.class,
()->calculator.division(1,0));
//위의 exception에서 message를 가져와 비교하였다.
assertEquals("/ by zero",exception.getMessage());
}
@Test
void timeoutNouExceeded(){
//테스트 수행 시간을 지정한다 첫번째 인자로는 수행시간이고 , 두번째 인자로는 수행할 테스트이다.
assertTimeout(ofSeconds(1),()->{
//2분안에 실행될 수 있는 것은 통과하고 그렇지 못하다면 테스트에 실패한다.
Thread.sleep(2000);
});
}
@Test
void timeoutNotExceededWithResult() {
//테스트 실행후 리턴값을 넘겨줄 수도 있다.
//아래의 경우 만약 테스트가 2분아래라면 a result라는 문자열을 넘겨주게 되어있다.
String actualResult = assertTimeout(ofMinutes(2), () -> {
System.out.println("테스트");
return "a result";
});
assertEquals("a result", actualResult);
}
//클래스 내의 static method를 불러와서 사용할 수도 있다.
@Test
void timeoutNotExceededWithMethod() {
// 2번째 인자의 메서드가 지정한 시간내에 실행 되었을 경우 테스트에 성공하고 해당 메서드의 리턴값을 리턴해준다.
String actualGreeting = assertTimeout(ofMinutes(2), AssertionTest::greeting);
assertEquals("Hello, World!", actualGreeting);
}
private static String greeting() {
return "Hello, World!";
}
}
참조
junit.org/junit5/docs/current/user-guide/
'JAVA' 카테고리의 다른 글
JAVA 스터디 8 - 상속 (0) | 2021.03.18 |
---|---|
JAVA 스터디 7 - 자료구조 LinkedList , Stack , Queue (0) | 2021.02.13 |
JAVA 스터디 5 - switch expression (자바 12부터 추가됨) (0) | 2021.02.05 |
JAVA 스터디 4- 제어문,반복문 (0) | 2021.02.03 |
JAVA 스터디 3 - 연산자 (0) | 2021.02.03 |