새소식

인기 검색어

개발일기

Kotlin에서 RestAssured 사용하기

  • -

Kotlin에서 RestAssured 사용하기

이번에 토이 프로젝트를 하면서 Kotlin + RestAssured 조합을 처음 다뤄보게 되었다.

 

RestAssured를 사용하며 인수테스트를 작성할 때, 망규님이 작성하신 양식을 조금 변형해서 사용하고 있었는데, 이번에 Kotlin에서 사용하려다가 문제를 겪어서 그 이야기를 해볼까 한다

톰캣을 실제로 띄우는 RestAssured 환경에서는 스프링 컨테이너와 서블릿 컨테이너(톰캣) 다른 쓰레드에서 실행되기 때문에 @Transactional의 롤백이 동작하지 않는다.

 

따라서 테스트 메서드 마다 테이블의 레코드들을 모두 정리하는 쿼리를 날려서 해결하는데, 이를 @AcceptanceTest 라는 애노테이션으로 만들어 해결한다.

 

내가 만든 Kotlin용 템플릿은 다음과 같다

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Retention(AnnotationRetention.RUNTIME)
@TestExecutionListeners(value = [AcceptanceTestExecutionListener::class], mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
annotation class AcceptanceTest()
class AcceptanceTestExecutionListener : AbstractTestExecutionListener() {
    override fun beforeTestMethod(testContext: TestContext) {
        val environment = testContext.applicationContext.environment
        val serverPort = environment.getProperty("local.server.port", Int::class.java)
        RestAssured.port = serverPort ?: 0
        val jdbcTemplate = getJdbcTemplate(testContext);
        val truncateQueries = getTruncateQueries(jdbcTemplate);
        truncateTables(jdbcTemplate, truncateQueries);
    }
...
}

망규님의 템플릿과의 차이점은, 망규님의 경우 afterTestMethod로 쿼리를 날렸지만, 내 생각에는 어차피 처음에는 테이블이 비워져 있고, 마지막 인수테스트 이후에는 쿼리를 날릴 필요가 없어서 beforeTestMethod로 변경했다.

 

또한, RestAssured.port에 포트 번호를 넣는 동작을 추가적으로 수행하고 있다. (망규님 코드에서는 port를 지정해주지 않아도 되던데 그 이유는 정확히 모르겠다)

 

처음에는 @LocalServerPort로 포트 번호를 주입하려 여러 시도를 해보았으나, 끝내 주입되지 않았다. 궁금해서 TestExecutionListener를 타고 들어가 봤더니 빈으로 등록하지 않고 있었다. POJO의 필드에서 스프링 빈을 주입받길 원하고 있었으니 당연히 주입되지 않았던 것이다.

 

빈으로 등록해서 사용하던가, 메서드로 전달받은 Context를 사용하던가

대신 메서드 주입으로 testContext를 받고 있었기 때문에, testContext에서 포트를 직접 꺼내어서 RestAssured.port에 지정해서 해결했다.

 

구글링 하다보니 비슷한 문제를 겪고 계신 분이 있으셔서 내가 해결한 해결책을 제안해 드리기도 했다.

추가적으로 Kotlin Rest Assured에서 재밌던 건, Extension Module을 사용하면 Given When Then을 굉장히 명시적으로 작성할 수 있게 된다는 점이다.

 

예를 들면 다음과 같다

val message: String =
Given {
    port(7000)
    header("Header", "Header")
    body("hello")
} When {
    put("/the/path")
} Then {
    statusCode(200)
    body("message", equalTo("Another World"))
} Extract {
    path("message")
}

물론 나같은 경우 외부 API 호출 부분은 TestSteps으로 감싸긴 하지만, 거기 안에서도 명시적으로 작성할 수 있게 된다는 점이 큰 메리트인 것 같다.

'개발일기' 카테고리의 다른 글

load average를 파악하는 uptime과 커널 메시지를 출력하는 dmesg  (0) 2023.08.26
파일 시스템  (0) 2023.08.25
가상 메모리  (0) 2023.08.23
스케줄링과 동기화  (0) 2023.08.20
Red Black Tree vs Heap vs AVL Tree  (0) 2023.08.17
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.