본문 바로가기

Spring/Test

AssertJ

 

 

1. AssertJ

JUnit에서 제공하는 assert() 메소드의 부족한 표현력을 충족시켜줌

타입별로 다양한 검증 메소드를 제공하여 테스트 코드를 더욱 쉽게 작성할 수 있게 도와줌

 

JUnit assert() 와 AssertJ 비교

id가 "a"를 포함하고 있는 지 여부 검사

// JUnit assert()사용 코드
assertTrue(id.contains("a"));

// 단언 실패 메시지, 실패 이유를 알 수 없음
org.opentest4j.AssertionFailedError: expected: <true> but was: <false>



// AssertJ 사용 코드, id가 "a" 를 포함하는 지 여부를 검사한다는 것을 직관적으로 확인할 수 있음
assertThat(id).contains("a");

// 단언 실패 메시지, "bcd"가 "a"를 포함할거라고 기대했지만 그렇지 않다라는 것을 알려줌
java.lang.AssertionError:
Expecting:
<"bcd">
to contain:<"a">

 

 

2. 의존 설정

testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1'

 

3. 사용법

assertThat(실제값).검증메소드(기대값);

// 여러 검증메소드를 검증할 수 있음
assertThat(실제값)
        .검증메소드1(기대값)
        .검증메소드2(기대값)
        .검증메소드3(기대값);

 

4. assertThat() 메소드 

1) 기본 검증 메소드

 

- 모든 타입에 사용할 수 있는 검증 메소드

@Test
@DisplayName("검증 메소드 사용법")
public void basicTest() {
    assertThat("Hello, world! Nice to meet you.") // 주어진 "Hello, world! Nice to meet you."라는 문자열은
            .isEqualTo("Hello, world! Nice to meet you.")// "Hello, world! Nice to meet you."과 일치합니다.
            .isNotEmpty() // 비어있지 않고
            .contains("Nice") // "Nice"를 포함하고
            .contains("world") // "world"도 포함하고
            .doesNotContain("ZZZ") // "ZZZ"는 포함하지 않으며
            .startsWith("Hell") // "Hell"로 시작하고
            .endsWith("u.") // "u."로 끝나며
; 
}
  • isEqualTo(값) : 값과 같은지 검증한다.
  • isNotEqualTo(값) : 값과 같지 않은지 검증한다.
  • isNull() : null인지 검증한다
  • isNotNull() : null이 아닌지 검증한다.
  • isIn(값 목록) : 값 목록에 포함되어 있는지 검증한다.
  • isNotIn(값 목록)

*isIn(), isNotIn()의 값 목록은 가변 인자로 주거나 List와 타입(정확히는 Iterable을 구현하는 타입)을 이용해서 전달

 

 

- Comparable 인터페이스를 구현한 타입이나 int, double과 같은 숫자 타입의 경우 사용할 수 있는 검증 메소드

  • isLessThan(값) : 값 보다 작은지 검증한다.
  • isLessThanOrEqualTo(값) : 값보다 작거나 같은지 검증한다.
  • isGreaterThan(값) : 값 보다 큰지 검증한다.
  • isGreaterThanOrEqualTo(값) : 값보다 크거나 같은지 검증한다.
  • isBetween(값1, 값2) : 값1과 값2 사이에 포함되는지 검증한다.
 
 

2) String에 대한 추가 검증 메서드

 

- 특정 값을 포함하는지 검사하는 메소드
 
    @Test
    @DisplayName("String 타입, 특정값 포함 검증 메소드")
    public void stirngTypeTest() {
        assertThat("String Type Assert") // 실제값
                .contains("String Type Assert") // 문자열 전체를 포함하는지 검증 
                .contains("String") // 문자열 일부를 포함하는지 검증
                .contains(" ") // 문자열의 일부 중 공백을 포함하는지 검증
                .containsOnlyOnce("String")
                .containsWhitespaces()
//                .containsOnlyDigits()
//                .containsOnlyWhitespaces()
        ;
    }
  • contains(CharSequance... values) : 인자로 지정한 문자열들을 모두 포함하고 있는지 검증한다.
  • containsOnlyOnce(CharSequance sequance) : 해당 문자열을 딱 한 번만 포함하는지 검증한다.
  • containsOnlyDigits() : 숫자만 포함하는지 검증한다.
  • containsWhitespaces() : 공백 문자를 포함하고 있는지 검증한다.
  • containsOnlyWhitespaces() : 공백 문자만 포함하는지 검증한다.
 

- 특정 값을 포함하지 않는지 검사하는 메소드

    @Test
    @DisplayName("String 타입, 특정값 미포함 검증 메소드")
    public void stirngType2Test() {
        assertThat("String Type Assert") // 실제값
                .doesNotContain("String Type2 Assert") // 문자열 전체 미포함 검증, 이 문자열을 포함하지 않지?
//                .doesNotContainAnyWhitespaces() // 공백 미포함 검증, 문자열에 공백이 없지?
                .doesNotContainOnlyWhitespaces() // 공백만 미포함 검증, 공백만 있는 문자열 아니지?
                .doesNotContainPattern("^[가-힣]*$") //  정규표현식 미포함 검증, 한글을 포함하고 있니? , pattern(): 컴파일된 정규표현식을 String 형태로 반환
                .doesNotContainPattern(Pattern.compile("St...n")); // 정규표현식 미포함 검증, compile(regex)메소드 사용: 주어진 정규표현식으로 패턴을 만든다
    }
  • doesNotContain(CharSequence... values) : 인자로 지정한 문자열들을 모두 포함하고 있지 않은지 검증한다.
  • doesNotContainAnyWhitespaces() : 공백 문자를 포함하고 있지 않은지를 검증한다.
  • doesNotContainOnlyWhitespaces() : 공백 문자만 포함하고 있지 않은지를 검증한다.
  • doesNotContainPattern(Pattern pattern) : 정규 표현식에 일치하는 문자를 포함하고 있지 않은지를 검증한다.
  • doesNotContainPattern(CharSequence pattern) : 정규 표현식에 일치하는 문자를 포함하고 있지 않은지를 검증한다.

 

- 특정 문자열로 시작하거나 끝나는지 검사하는 메소드
  • startsWith(CharSequance prefix) : 지정한 문자열로 시작하는지를 검증한다.
  • doesNotStartWith(CharSequence prefix) : 지정한 문자열로 시작하지 않는지를 검증한다.
  • endsWith(CharSequence suffix) : 지정한 문자열로 끝나는지를 검증한다.
  • doesNotEndWith(CharSequence suffix) : 지정한 문자열로 끝나지 않는지를 검증한다.

 

 

3) 숫자에 대한 추가 검증 메소드

  • isZero() / isNotZero() : 0인지 또는 0이 아닌지를 검증한다.
  • isOne() : 1 인지를 검증한다.
  • isPositive() / isNotPositive() : 양수인지 또는 양수가 아닌지를 검증한다.
  • isNegative() / isNotNegative() : 음수인지 또는 음수가 아닌지를 검증한다.

 

 
4) 날짜/시간에 대한 검증 메소드
 
LocalDateTime regDt = user.getRegDt();
assertThat(regDt).ifAfter(LocalDateTime.of(2022, 11, 30, 23, 59, 59));

 

 

- 날짜와 시간을 비교하는 주요 메소드

  • isBefore(비교할 값) : 비교할 값보다 이전인지 검증한다.
  • isBeforeOrEqualTo(비교할 값) : 비교할 값보다 이전이거나 같은지 검증한다.
  • isAfter(비교할 값) : 비교할 값보다 이후인지 검증한다.
  • isAfterOrEqualTo(비교할 값) : 비교할 값보다 이후이거나 같은지 검증한다.
 

- LocalDateTime, OffsetDateTime, ZonedDateTime 타입에 대한 추가 검증 메소드

  • isEqualToIgnoringSeconds(비교할 값) : 초 이하 시간을 제외한 나머지 값이 같은지 검증한다. 즉 분 단위까지 값이 같은지 검증한다.
  • isEqualToIgnoringMinutes(비교할 값) : 분 이하 시간을 제외한 나머지 값이 같은지 검증한다. 즉 시 단위까지 값이 같은지 검증한다.
  • isEualToIgnoringHours(비교할 값) : 시 이하 시간을 제외한 나머지 값이 같은지 검증한다. 즉 일 단위까지 값이 같은지 검증한다.

 

 

5) 콜렉션에 대한 검증 메소드

 

- List, Set 등 콜렉션에 대한 주요 검증 메소드
  • contains(E ... values) : 콜렉션이 지정한 값을 포함하는지 검증한다.
  • containsOnly(E ... values) : 콜렉션이 지정한 값 중 일부를 포함하는지 검증한다.
  • containsOnlyOnce(E ... values) : 콜렉션이 지정한 값을 한 번만 포함하는지 검증한다.

 

- Map을 위한 주요 검증 메소드
  • containsKey(K Key) : Map이 지정한 키를 포함하는지 검증한다.
  • containsKeys(K... Keys) : Map이 지정한 키들을 포함하는지 검증한다.
  • containsOnlyKeys(K... Keys) : Map이 지정한 키만 포함하는지 검증한다.
  • doesNotContainKeys(K... Keys) : Map이 지정한 키들을 포함하지 않는지 검증한다.
  • containsValues(VALUE... values) : Map이 지정한 값들을 포함하는지 검증한다.
  • contains(Entry<K,V> ... values) : Map이 지정한 Entry<K,V>를 포함하는지 검증한다.

 

 
 

6) 익셉션 관련 검증 메소드

  • assertThatThrownBy() : 인자로 받은 람다에서 익셉션이 발생하는지 검증한다.
assertThatThrownBy(() -> new File("nofile.txt"));

 

 

  • isInstanceOf() : 익셉션의 타입을 추가로 검증할 수 있다.
assertThatThrownBy(() -> new File("nofile.txt"));
	.isInstanceOf(IOException.class);

 

 

  • assertThatExceptionOfType() : 발생할 익셉션의 타입을 지정하고 isThrownBy() 메서드를 이용해서 익셉션이 발생할 코드 블록을 지정할 수 있다.
assertThatExceptionOfType(IOException.class)
    .isThrownBy(() -> {
    	readFile(new File("nofile.txt"));
    });

 

  • assertThatIOExceptionOfType() : IOException이 발생하는지 검증한다.
assertThatIOExceptionOfType(IOException.class)
    .isThrownBy(() -> {
    	readFile(new File("nofile.txt"));
    });
    

// assertThatIOException() 메소드 외에 
// assertThatNullPointerException()
// assertThatIllegalArgumentException()
// assertThatIllegalStateException() 메소드를 제공

 

  • doesNotThrowAnyException() : 익셉션이 발생하지 않는 것을 검증한다.
assertThatCode(() -> {
	readFile(new File("pom.xml"));
}).doesBotThrowAnyException();

 

 

5. SoftAssertions로 모아서 검증하기

JUnit 5의 assertAll()과 유사하다. 여러 검증을 한 번에 수행하고 싶을 때 사용한다.

@Test
@DisplayName("SoftAssertions")
public void softTest() {
    SoftAssertions soft = new SoftAssertions();
    soft.assertThat(1).isBetween(0, 2);
    soft.assertThat(1).isGreaterThan(2); // 1이 2보다 큰지 검증하는 코드, 검증을 통과할 수 없지만 코드 실행 시점에는 검증 실패가 발생하지 않음
    soft.assertThat(1).isLessThan(0);
    soft.assertAll(); // 실제 검증은 assertAll()메소드를 실행할 때 진행
}

-> 실행결과에 검증하는 대상 중 두 개가 검증에 실패했다는 내용과 각 실패 대상 위치를 알려줌

 

 

 

  • SoftAsserrtions.assertSoftly() 정적 메소드 : assertAll()메소드를 직접 호출하지 않아도 된다.
SoftAssertions.assertSoftly(soft -> {
            soft.assertThat(1).isBetween(0, 2);
            soft.assertThat(1).isGreaterThan(2);
            soft.assertThat(1).isLessThan(0);
        });

 

 

6. as()와 describedAs()로 설명 달기

 

  • as(): 테스트에 설명을 붙인다. as()메소드로 지정한 설명 문구가 실패 메시지에 표시된다.

 

- as() 메소드 사용

@Test
@DisplayName("as()Test")
public void asTest() {
    userRegister.register("id", "pw", "email");

    User savedUser = fakeRepository.findById("id");

    assertThat(savedUser.getId())
            .as("ID 검사")
            .isEqualTo("abc");
}

 

 

- as()메소드에는 문자열 포맷을 사용할 수 있음, 첫 번째 인자는 포맷팅을 포함한 문자열, 두 번째 인자부터는 포맷팅에 사용할 값 전달

@Test
@DisplayName("as()Test")
public void asTest2() {
    userRegister.register("id", "pw", "email");

    User savedUser = fakeRepository.findById("id");

    assertThat(savedUser.getId())
            .as("ID 검사: %s", "abc")
            .isEqualTo("abc");
}

 

 

 

- 한 테스트에서 다수의 검증 메소드를 실행할 때 as()를 유용하게 사용할 수 있음,  검증 실패하는 단언이 있을 시 ret[] 으로 몇 번째 값이 검증에 실패했는지 알 수 있음

List<Integer> ret = getResults();

    List<Integer> expected = Arrays.asList(1, 2, 3);
    SoftAssertions soft = new SoftAssertions();
    for (int i = 0 ; i < expected.size() ; i++) {
        soft.assertThat(ret.get(i)).as("ret[%d]", i).isEqualTo(expected.get(i));
    }
    soft.assertAll();

 

 

  • describedAs(): as()메소드 대신 사용할 수 있다.

 

 

 

 

 

 

*전체 메소드는 공식문서 확인

https://www.javadoc.io/static/org.assertj/assertj-core/3.23.1/org/assertj/core/api/AbstractAssert.html#method.summary

 

AbstractAssert (AssertJ fluent assertions 3.23.1 API)

Type Parameters: SELF - the "self" type of this assertion class. Please read "Emulating 'self types' using Java Generics to simplify fluent API implementation" for more details. ACTUAL - the type of the "actual" value. All Implemented Interfaces: Assert ,

www.javadoc.io

 

 

 

https://assertj.github.io/doc/

 

AssertJ - fluent assertions java library

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, BJ Hargrave, Jeremy Landis, Ashley Scopes, Roland Weisleder , Benedikt Bogason , Andreas Kutschera , Matthew , Chris HeZean , Leo0506 , Zhou Yicheng , Saria , Chunhao Liao , max

assertj.github.io

 

참고: 최범균의 테스트 주도개발 시작하기