본문 바로가기
dev/kotlin

[Kotlin] REST Assured로 통합 테스트 작성하기

by igooo 2024. 9. 25.
728x90

개요

Java 프로젝트에서 자주 사용하던 REST Assured(https://rest-assured.io/)를 사용하여 Kotlin 프로젝트에서요 실제 API를 호출하는 Integraion Test를 작성한다

 

Java REST Assured 참고 : https://blog.igooo.org/119 

 

Why Kotlin?

Java 

Java로 Junit 테스트를 작성하는 경우 여러 줄의 검증 코드가 있을 때 특정줄의 검증 코드에서 오류가 발생하면 아래 검증 코드는 실행되지 않고 사용자에게 오류 라인수를 리포팅해 준다.

......
then().
    statusCode(200).
    body("size", is(3)).
    body("name.any { it == 'Ervin Howell' }", is(true)).
    body("find { user -> user.username == 'igooo' }.company.name", equalTo("Company"))

위 코드에서 body("size", is(3)) 라인에서 실패가 발생하면 Java의 경우 아래 라인에 대해서는 검증코드가 작동하지 않는다. 테스트 코드를 작성 중인 경우 실패한 코드를 수정하고 다시 테스트를 수행하여 코드를 검증해야 하며, Spring을 연동하는 경우 다시 테스트를 수행하는 경우 몇 초의 시간이 필요할 수 있다.

......
then().
    statusCode(200).
    body(
        "size", is(3),
        "name.any { it == 'Ervin Howell' }", is(true),
        "find { user -> user.username == 'igooo' }.company.name", equalTo("Company")
    )

REST Assured에서는 "multi-body expectations" 개념을 사용하여 위 코드처럼 변경하면 body 구문안에 코드를 한 번에 검증해 주지만 여전히 body 구문 위에 statusCode(200)에서 오류가 발생한다면 여전히 body 구문의 검증은 실행되지 않을 것이다.

Kotlin

Given {
    queryParam("deleted", false)
} When {
    get("/users")
} Then {
    statusCode(200)
    body("size()" is(3))
    body("name.any { it == 'Ervin Howell' }", is(true)).
    body("find { user -> user.username == 'igooo' }.company.name", equalTo("Company"))
} Extract {
   path("age
}

Kotlin으로 코드를 작성하면 Java에서 발생한 문제와 추가적으로 Code Formatter 문제 모두를 해결할 수 있다. Kotlin은 검증 코드가(statusCode, body) Then  구문 안에 정의되어 있기 때문에 한 번에 모든 에러를 리포팅해 준다.

Given, When, Then, Extract이 대문자인 이유는 Kotlin 예약어 때문에 대문자로 작성한다.

 

 

Getting Started

Kotlin 확장 모듈 

REST Assured는 Kotlin 지원을 위한 "kotlin-extension" 모듈을 제공한다.

Spring Boot와 REST Assured 모듈을 추가해 준다.

plugins {
	kotlin("jvm") version "1.9.25"
	kotlin("plugin.spring") version "1.9.25"
	id("org.springframework.boot") version "3.3.4"
	id("io.spring.dependency-management") version "1.1.6"
}

group = "org.igooo"
version = "0.0.1-SNAPSHOT"

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(21)
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	
	// REST Assured
	testImplementation("io.rest-assured:kotlin-extensions:5.5.0")
	testImplementation("io.rest-assured:json-path:5.5.0")
	
	testImplementation("org.springframework.boot:spring-boot-starter-test")
	testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
	testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

kotlin {
	compilerOptions {
		freeCompilerArgs.addAll("-Xjsr305=strict")
	}
}

tasks.withType(JavaCompile::class) {
	options.relesase.set(21)
}

tasks.withType<Test> {
	useJUnitPlatform()
}

 

API Test Code

개발 환경 구분을 위해서 @Tag를 사용하고, 사용자 아이디로 사용자 정보를 조회하여 검증하는 테스트 코드다. 추가로 API 주소를 Value로 받아서 처리하도록 @ParameterizedTest를 사용하여 테스트를 작성했다.

import io.restassured.http.ContentType
import io.restassured.module.kotlin.extensins.Given
import io.restassured.module.kotlin.extensins.Then
import io.restassured.module.kotlin.extensins.When
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.notNullValue
import org.junit.jupiter.api.Tag
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.Params.provider.ValueSource

@Tag("dev")
class DevAPITests {
    @ParameterizedTest
    @ValueSource(strings = ["dev_api_url"]) {
    fun 'DEV /users/{id}'(apiServer: String) {
        val id = 1
        
        Given {
            pathParam("id", id)
            queryParam("deleted", false)
            // log().all()
        } When {
            get("${apiServer}/users/{id}")
        } Then {
            statusCode(200)
            contentType(ContentType.JSON)
            body("id", `is`(id))
            body("name", equalTo("igooo"))
            body("address", notNullValue())
            // log().all()
        }
        
    }
}

 

Gradle Junit5 Tag 적용

Maven을 사용하는 경우 Maven 실행 시 group 옵션을 CLI에 추가하여 Tag값을 실행할 때 적용할 수 있었는데 Gradle에서는 해당 방법을 찾지 못해서 아래와 같이 개발 환경별 Tag를 적용할 수 있도록 Gradle에 스크립트를 추가해 준다.

tasks.register<Tast>("liveTest") {
    userJunitPlatform {
        includeTags("live")
    }
}    

tasks.register<Tast>("devTest") {
    userJunitPlatform {
        includeTags("dev")
    }
}

 

Gradle로 테스트 실행 시 아래와 같이 망별 테스트 task 명으로 실행한다.

$ ./gradlew devTest

 

참고

'dev > kotlin' 카테고리의 다른 글

Gradle JUnit 5 Tag  (0) 2024.11.03