티스토리 뷰

반응형

테스트 코드 작성을 위한 기술 공부의 필요성을 느껴 관련 내용을 시리즈로 정리해 볼려고 합니다.

이번 글은 그 처음 시작인 AssertJ에 관한 글로 시작하겠습니다.

 

테스트 코드 공부 시리즈

1. AssertJ를 이용한 테스트 코드 작성

2. JUnit의 Parameterized 어노테이션 사용하기

3. Spring에서 지원하는 AutoConfigure Annotation 알아보기

4. Mockito 톺아보기 w.kotlin

5. kotlin에 특화된 mockito-kotlin 사용하기 

6. postman을 이용한 시나리오 테스트 하기

 

AssertJ 값 비교


AssertJ에서는 값 비교를 할때 대부분 isEqualTo() 를 사용한다.

@Test
fun `동일성 isEqualTo 비교`(){
    val student1 = Student("Kia", 30)
    val student2 = Student("Kia", 30)

    // isEqualTo 메서드를 이용해 값 비교
    assertThat(student1.name).isEqualTo("Kia")
}

isEqualTo 메서드를 사용하면 매우 쉽게 값을 검증할 수 있게 된다.

여기서 한가지 주의해야 할 점은 isEqualTo() 메서드는 동일성 비교를 한다는 점이다.

 

동일성 비교 의외에는 동등성 비교가 있는데 두가지의 차이점을 알아보자.

 

동일성 vs 동등성

동일성

  • 동일성의 경우에는 값 뿐만 아니라 완전히 같은 객체인지를 확인 한다.

동등성

  • 동등성의 경우에는 보유하고 있는 값이 같은지를 확인한다.
@Test
fun `동일성 isEqualTo 비교`(){
    val student1 = Student("Kia", 30)
    val student2 = Student("Kia", 30)

    // 동일성 비교, 실패
    assertThat(student1).isEqualTo(student2)

    // 동등성 비교, 성공
    assertThat(student1).usingRecursiveComparison().isEqualTo(student2)
}

student1 객체와 student2 객체는 보유하고 있는 값은 동등성을 보장하지만 동일성은 보장하지 않는다.

isEqualTo() 메서드를 통해 비교시 동일성 비교를 하기 때문에 실패하는 것을 볼 수 있다.

 

AssertJ에서는 기본값이 동일성 비교이기 때문에 동등성 비교를 하고 싶은 경우 usingRecursiveComparison() 메서드를 사용해 동등성 비교도 가능하다.

 

AssertJ 권장 사용 방법

AssertJ에서는 검증시에 주석, 설명등을 추가 할 경우에 isEqualTo() 메소드 앞쪽에 위치시켜 사용하는 것을 권장하고 있다.

// 잘못된 사용 방법   assertion 이후에는 as/describedAs가 아무 영향이 없다.
assertThat(actual).isEqualTo(expected).as("description")
assertThat(actual).isEqualTo(expeceted).describedAs("description")

// 올바른 사용 방법
assertThat(actual).as("description").isEqualTo(expected)
assertThat(actual).describedAs("description").isEqualTo(expceted)
// 잘못된 사용 방법   assertion 이후에는 overridingeErrorMessage/withFailMessage 가 아무 영향이 없다.
assertThat(actual).isEqualTo(expected).overridingErrorMEssage("custom error message")
assertThat(actual).isEqualTo(expeceted).withFailMessage("custom error message")

// 올바른 사용 방법
assertThat(actual).overridingErrorMEssage("custom error message").isEqualTo(expected)
assertThat(actual).withFailMessage("custom error message").isEqualTo(expceted)

 

Extracting을 이용한 검증


테스트시 리스트의 객체 필드 값을 검증하기 위해서는 반복문에서 필드 값을 꺼내와 또 다른 리스트에 담고 비교하는 불편한 과정을 거쳐야 한다.

이러한 과정을 extracting()을 사용하면 간단하게 검증이 가능하다.

 

extracting을 이용한 프로퍼티 검증

@Test
fun `extracted 를 이용한 프로퍼티 검증`() {
    val list = mutableListOf<Student>()
    val audi = Student("Audi", 30)
    val kia = Student("Kia", 20)
    val bmw = Student("Bmw", 25)
    val tesla = Student("Tesla", 5)

    list.add(audi)
    list.add(kia)
    list.add(bmw)
    list.add(tesla)

    // 필드를 추출하고 값 검증
    assertThat(list).extracting("name").contains("Audi", "Kia", "Bmw", "Tesla")
    assertThat(list).extracting("name", String::class.java).contains("Audi", "Kia", "Bmw", "Tesla")
}

 

Filtering을 이용한 검증


assertThat(fellowshipOfTheRing).filteredOn ( character -> character.getName().contains("o") )
                               .containsOnly(aragorn, frodo, legolas, boromir);

// filters 로 property/field values 검증
assertThat(fellowshipOfTheRing).filterdOn("race", HOBBIT)
                               .containsOnly(sam, frodo, pippin, merry);

 

필터링을 이용한 다양한 검증

@Test
fun `filter 프로퍼티 검증`() {
    val list = mutableListOf<Student>()
    val audi = Student("Audi", 30)
    val kia = Student("Kia", 20)
    val bmw = Student("Bmw", 25)
    val tesla = Student("Tesla", 5)

    list.add(audi)
    list.add(kia)
    list.add(bmw)
    list.add(tesla)

    // 프로퍼티 검증
    assertThat(list).filteredOn("age", 25)
        .containsOnly(bmw)

    // 간단한 검증 방법
    assertThat(list).filteredOn("name", `in`("Kia"))
        .containsOnly(kia)
    assertThat(list).filteredOn("name", `not`("Kia"))
        .containsOnly(audi, bmw, tesla)
    assertThat(list).filteredOn("name", notIn("Kia", "Bmw"))
        .containsOnly(audi, tesla)
}

filteredOn에서 프로퍼티를 검증할 때 in, not, notIn을 통해서 검증 할 수 있다.

 

 

람다식을 이용한 필터링

@Test
fun `filter 람다표현식 검증`() {
    val list = mutableListOf<Student>()
    val audi = Student("Audi", 30)
    val kia = Student("Kia", 20)
    val bmw = Student("Bmw", 25)
    val tesla = Student("Tesla", 5)

    list.add(audi)
    list.add(kia)
    list.add(bmw)
    list.add(tesla)

    assertThat(list).filteredOn { student -> student.name.contains("a") }
        .containsOnly(kia, tesla)
    assertThat(list).filteredOn { student -> student.age <= 25 }
        .containsOnly(kia, bmw, tesla)
}

 

File 검증


@Test
fun `file 내용(content) String 값 검증`() {
    val xFile = writeFile("xFile", "The Truth Is Out There")

    // 파일이 존재하는지, 상대 경로인지 테스트
    assertThat(xFile).isFile.isRelative

    // 파일의 내용을 contentOf로 가져와 테스트
    assertThat(contentOf(xFile)).startsWith("The Truth")
                                .contains("Is Out")
                                .endsWith("There")
}

private fun writeFile(fileName: String, fileContent: String): File {
    val file = File(fileName)
    val writer = BufferedWriter(OutputStreamWriter(FileOutputStream(file), Charset.defaultCharset()))
    writer.write(fileContent)
    writer.close()
    return file
}

AssertJ는 파일에 대한 테스트 메서드를 제공합니다.

  • isFile : 파일이 존재하는지 여부를 검증
  • contentOf: 파일 내용을 가져온다

 

이 밖에도 다양한 File 타입에 사용가능한 메서드가 있습니다.

이러한 메서드들은 이런게 있었지 하고 기억만 해두시다가 필요할 때에 여기에서 찾아서 사용하시면 됩니다.

 

 

Exception 검증


BDD스타일의 검증

@Test
fun `BDD 스타일 예외 처리 방식`() {
    // when
    val catchThrowable = catchThrowable { throw Exception("boom!") }

    // then
    assertThat(catchThrowable).isInstanceOf(Exception::class.java)
        .hasMessageContaining("boom")
}

BDD 테스트 스타일은 테스트 코드를 Given, When, Then 패턴을 통해 테스트 코드를 작성하는것입니다.

이러한 패턴은 시나리오에 맞게 테스트 코드가 읽히도록 해주는 장점이 있습니다.

 

 

AssertJ는 좀 더 가독성이 좋은 예외 처리 방식을 지원합니다.

 

assertThatThrownBy() 를 활용한 예외 검증

@Test
fun `assertThatThrownBy 를 사용한 예외 처리`() {
    assertThatThrownBy{throw Exception("boom!")}.isInstanceOf(Exception::class.java)
        .hasMessageContaining("boom")
}

위의 BDD 스타일보다 간결하게 테스트 코드를 작성할 수 있는데요.

 

사실 BDD 스타일 검증보다 가독성이 좋은지에 대해서는 애매한 부분이 있습니다. assertThatThrownBy를 사용해 코드는 좀 더 간결해졌지만 When,Then이 확실히 구분이 되어 있는 테스트 코드가 코드를 읽을때는 더 쉬울거 같다는 생각이 들기도 합니다.

 

이 부분은 개인적인 취향인 부분이 큰거 같아

내가 평소에 BDD스타일로 주로 테스트 코드를 짜거나 BDD스타일로 테스트코드를 짜는 환경이라면 catchThrowable을 이용하고, 

간단한 테스트를 작성하거나 코드를 좀 더 간결하게 작성하고 싶으시면 assertThatThrownBy()를 사용하시면 좋을거 같습니다.

 

추가로 AssertJ에서 자주 발생하는 예외에 대해서는 함수를 지원해주고있습니다.

 

정의된 예외 처리 함수 사용

@Test
fun `자주 쓰이는 예외 처리 method`() {
    assertThatIOException().isThrownBy { throw IOException("boom!") }
        .withMessage("%s!", "boom")  // 변수를 이용해 동적으로 message를 검증 가능
        .withMessageContaining("boom")
        .withNoCause()  // throw 여부 확인
}

지원하는 함수의 종류는 4가지 입니다.

  • assertThatNullPointerException
  • assertThatIllegalArgumentException
  • assertThatIllegalStateException
  • assertThatIOException

 

만약에 4가지에 해당하지 않는 예외를 처리하기 위해서는 아래와 같은 방법으로도 검증이 가능합니다.

@Test
fun `지원하지 않는 예외 타입 처리`() {
    assertThatExceptionOfType(IOException::class.java).isThrownBy { throw IOException("boom!") }
        .withMessage("%s!", "boom")  // 변수를 이용해 동적으로 message를 검증 가능
        .withMessageContaining("boom")
        .withNoCause()
}

 

예외를 던지지 않는 경우 검증

@Test
fun `예외를 던지지 않는 경우`() {
    // 1번
    assertThatCode {
        print("예외를 던지지 않는 경우 테스트")
    }.doesNotThrowAnyException()

    // 2번
    assertThatNoException().isThrownBy { print("예외를 던지지 않는 경우 테스트") }
}

총 2가지 방식으로 검증이 가능하다.

다만 AssertJ 공식 홈페이지 highlight에 보면 1번으로 소개가 되어 있고 개인적으로도 1번이 코드를 순서대로 읽으면서 흐름을 파악할 수 있어서 가독성 측면에서 더 좋은 코드 작성방법인거 같습니다.

 

 

Junit5를 이용한 예외 검증

@Test
fun `Junit5를 이용한 예외 처리`() {
    // 예외가 발생 X
    assertDoesNotThrow { print("예외를 던지지 않는 경우 테스트") }

    // 예외 검증
    val exception = assertThrows<IOException> { throw IOException("boom!") }
    assertThat(exception.message).isEqualTo("boom!")
}

AssertJ를 공부하고 있는데 왜 갑자기 Junit5? 라고 의아해 하실 수 있는데요.

사실 이번에 공부를 하면서 처음 알았던 부분중에 하나가 여태까지 예외 검증 방식은 Junit5 방식을 사용하고 있었다는 것이었습니다.

앞으로는 헷갈리지 않고 테스트 코드를 작성하고자 내용을 추가해두었습니다.

 

마무리

AssertJ를 이용한 테스트 코드 작성 방법을 한번 쭉 정리해 보았는데요.

다음 글에서는 Junit5의 Paramiterzied에 관해 정리하는 글로 찾아뵙겠습니다!!

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함