참고 : https://junit.org/junit5/docs/current/user-guide/
https://donghyeon.dev/junit/2021/04/11/JUnit5-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C/
https://howtodoinjava.com/junit5/junit-5-assertions-examples/
JUnit Platform + JUnit Jupiter + JUnit Vintage
Junit5는 Java8 버전이상을 필요로 합니다.
@DisplayNameGeneration이 있을 경우 , DisplayName이 우선 적용된다.
Standard : 메소드이름과 괄호 까지 표시
Simple : 메소드 이름만
ReplaceUnderscores : 언더스코어를 제거하고 공백으로.
IndicativeSentenses : 클래스 이름 + 메소드 이름 + 괄호
ex)
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)의 경우
- test_service -> test service
참고
만일 , DisplayName과 DisplayNameGeneration이 있을 경우 , DisplayName이 우선 적용된다.
BeforeAll , AfterAll 는 static 메소드로 작성해야 한다.
class에 @TestInstance(TestInstance.Lifecycle.PER_CLASS)라고 붙여준다면, static 메소드가 아니어도 동작한다.
@Nested
@Tag
@Disable
@Test
@Disabled("깨진 테스트 이유를 알기전까지는 비활성화")
void disabledTest(){
System.out.println("이걸 실행되면 안됨");
}
@Timeout
@ExtendWith
@RegisterExtension
@RepeatedTest
@RepeatedTest(10)
void repeatTest(){
System.out.println("test");
}
//테스트 10번 반복
@RepeatedTest(10)
void repeatTest2(RepetitionInfo repetitionInfo){
System.out.println(repetitionInfo.getCurrentRepetition() + "/" + repetitionInfo.getTotalRepetitions());
}
//테스트 10번 반복
//RepetitionInfo 를 이용하여 현재 반복횟수와 총 반복회수를 가져올 수 있다.
@DisplayName("스터디 만들기")
@RepeatedTest(value =10 , name ="{displayName}, {currentRepetition}/{totalRetetitions}")
void repeatTest(RepetitionInfo repetitionInfo){
System.out.println("test");
}
//value로 테스트 반복 횟수
//name으로 반복되는 테스트마다 이름을 붙여줄 수 있다.
@ParameterizedTest
반복 되는 테스트 마다 따른 값을 이용하고 싶은 경우.
1) @ValueSource 이용하기
@DisplayName("파리미터 테스트")
@ParameterizedTest(name= {index} {displayName} message = {0})
@ValueSource(strings= {"날씨가" , "너무" ,"덥네요"})
void parameterizedTest(String message){
System.out.println(message);
}
//해당 인자값들을 message로 바인딩 받아서 사용한다.
//index를 이용하여 횟수를 알 수 있고, 해당 인덱스로 테스트에서 사용하는 인자값을 참조할 수 있다.
//ints등 다양한 Primitive type을 지원한다.
//하나의 인수만을 전달할 때 사용한다는 점 주의.
//다른 예시 객체로 받기
static class Study{
String username;
}
staic class StudyConverter extends SimpleArgumentConverter{
@Override
protected Object convert(Object source, Class<?> targetType) throws ArgumentConverter
Assertions.assertEquals(Study.class,targetType);
return new Study(source.toString());
}
@ParameterizedTest(name= {index} {displayName} message = {0})
@ValueSource(strings= {"홍길동" , "홍길동2" ,"홍길동3"})
void parameterizedTest(@ConvertWith(StudyConverter.class) Study study){
System.out.println(study.getName());
}
//study처럼 custom한 객체로 받고 싶다면 ,
SimpleArgumentConverter를 상속받아 구현하여 처리해줘야 한다.
2) @NullSource && @EmptySource
> 주의 @NullSource
```java
@ParameterizedTest
@ValueSource(strings= {"날씨가" , "너무" ,"덥네요"})
@EmptySource
@NullSource
void parameterizedTest(String message){
System.out.println(message);
}
//빈값을 넘겨주어 한번 호출해준다.
//null값을 넘겨 한번 호출한다.
//총 5번의 테스트가 호출된다.
//2개를 붙힌 @NullAndEmptySource를 이용해도 같은 결과다.
//참고 , @NullSource는 Primitive type에는 null값이 들어갈 수 없어서 실패한다.
```
3) @CsvSource
//여러개의 인자값을 넘겨주고자 할 때 사용할 수 있다.
//예시1
static class Study{
int age;
String username;
public Study(String username , int age){
this.username = username;
this.age = age;
}
}
@ParameterizedTest
@CsvSource({"홍길동" , "10" ,"홍길동2" , "20"})
void parameterizedTest(String username , int age ){
Study study = new Study(username, age);
System.out.println(study.getName());
}
//예시2
@ParameterizedTest
@CsvSource({"홍길동" , "10" ,"홍길동2" , "20"})
void parameterizedTest(ArgumentsAccessor arguments){
Study study = new Study(arguments.getString(0), arguments.getInteger(0));
System.out.println(study.getName());
}
//예시3
static class StudyAggregator implements ArgumentsAggregator{
@Override
public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) throws ArgumentsAggregationException {
Study study = new Study(arguments.getString(0), arguments.getInteger(0));
return study;
}
}
@ParameterizedTest
@CsvSource({"홍길동" , "10" ,"홍길동2" , "20"})
void parameterizedTest((@AggregateWith(StudyAggregator.class) Study study){
System.out.println(study.getName());
}
4)@EnumSource
열거형 값의 배열을 테스트에 전달한다.
하나의 인수만을 전달할 수 있다.
사용하고자 하는 Enum클래스를 넘겨주면 된다.
//예제1
@ParameterizedTest
@EnumSource(Enum.class)
void getValueEnum(Enum enum){
System.out.println(enum.getValue());
}
//예제2
@EnumSource(value=Enum.class , name={"EnumType1","EnumType2" })
name으로 특정 Enum타입값만 가져와 테스트할 수 있다.
5)@MethodSource
복잡한 Object를 전달하여 사용할 때 유용하다.
@ParameterizedTest
@MethodSource("studySetUpMethod")
void studyTest(Study study){
System.out.println(study.getUsername());
}
private static Stream<Arguments> studySetUpMethod(){
return Stream.of(
Arguments.of(new Study("홍길동1",10 ,"홍길동아이디1")),
Arguments.of(new Study("홍길동2",20 ,"홍길동아이디2"))
);
}
//static 메소드로 만들어줘야 한다.
//만일 메소드가 외부소스에 있다면 , 패키지명부터 적어줘야 한다
최상위 클래스, 스태틱 멤버 클래스, @Nested 클래스에 적어도 한개의 @Test 어노테이션이 달린 테스트 메소드가 포함되있는 걸 말한다
테스트 클래스는 abstract 이면 안되고 , 하나의 생성자가 있어야 한다.
- @Test ,@RepeatedTest ,@ParamterizedTest,@TestFactory ,@TestTemplate 같은 메타 어노테이션이 붙은 메소드
주의
테스트 메소드와 라이프사이클 메소드는 abstract 선언하면 안되고, 어떠한 값도 리턴되선 안된다.
Assertions.assertEquals(3 , add(1,2));
Assertions.assertEquals(4 , add(1,2));
@Test
public void arrayTest(){
int [] arr1 = {1,2,3};
int [] arr2 = {1,2,3};
Assertions.assertArrayEquals(arr1, arr2 , "일치하지 않습니다.");
//실패할 경우 메세지 지정
}
두 배열이 모두 null이어도 동일하다고 처리.
String str = null;
String str2 = "data";
assertNull(str);
assertNull(str2,"객체는 null이 아닙니다");
Assertions.assert(str2 , () -> "객체는 null이 아닙니다");
//Supplier 사용
}
String str1 = new String("스트링");
String str2 = new String("스트링");
Assertions.assertNotSame(str1 , str2);
//참
assertTrue(5 > 3);
assertTrue(5 < 3 , "5는 3보다 작습니다");
//실패메세지 지정
assertTrue(() -> 3 <5 , "5는 3보다 작습니다");
//BooleanSupplier 사용
@Test
void testFail(){
throws new IllegalArgumentException("잘못된 인자");
fail("IllegalArgumentException가 발생하지 않음")
}
@Test
public void arrayTest(){
assertAll(
"heading",
() -> assertEquals(1,3),
() -> assertEquals(1,2),
() -> assertEquals(1,4)
);
}
//결과
heading (3 failures)
org.opentest4j.AssertionFailedError: expected: <1> but was: <3>
org.opentest4j.AssertionFailedError: expected: <1> but was: <2>
org.opentest4j.AssertionFailedError: expected: <1> but was: <4>
@Test
void testAssertThrow(){
IllegalArgumentException exceptionInfo = assertThrows(IllegalArgumentException.class,
() -> {
throw new IllegalArgumentException("예외 발생");
});
assertEquals("예외 발생" , exceptionInfo.getMessage());
}
//결과를 꺼내올 수 있다.
@Test
void testTimeout(){
assertAll("timeOut",
() -> assertTimeout(ofSeconds(2),() -> Thread.sleep(1000)),
() -> assertTimeout(ofSeconds(2),() -> Thread.sleep(1000)),
() -> assertTimeoutPreemptively(ofSeconds(2),() -> Thread.sleep(1999)),
() -> assertTimeoutPreemptively(ofSeconds(2),() -> Thread.sleep(1999))
//2초 미만으로 수행되어야 종료되지 않음.
);
}
@Test
void assertIterableEquals(){
Iterable<Integer> listOne = new ArrayList<>(Arrays.asList(1,2,3,4));
Iterable<Integer> listTwo = new ArrayList<>(Arrays.asList(1,2,3,4));
Iterable<Integer> listThree = new ArrayList<>(Arrays.asList(1,2,3));
Iterable<Integer> listFour = new ArrayList<>(Arrays.asList(1,2,4,3));
Iterable<Integer> listFive = new LinkedList<>(Arrays.asList(1,2,3,4));
//Test will pass
Assertions.assertIterableEquals(listOne, listTwo);
//Test will pass(구현체가 다르더라도 통과)
Assertions.assertIterableEquals(listOne,listFive);
//Test will fail (갯수가 다르다고 실패)
Assertions.assertIterableEquals(listOne, listThree);
//Test will fail (순서가 다르다고 실패)
Assertions.assertIterableEquals(listOne, listFour);
}
List<String> expected = asList("Java", "\\d+", "JUnit");
List<String> actual = asList("Java", "11", "JUnit");
assertLinesMatch(expected, actual);