코틀린 하면서 처음에는 잘 이해하지 못했던 DSL 문법의 간결함을 알게 되면서
infix 로 연계하여 만들 수 있는 DSL 구조가 좋은 것 같다는 생각이 들었다.
예를 들면 원래 Java + QueryDSL 을 사용한다면 아래와 같은 코드가 작성될 수 있을 것이다.
selectFrom(person).where(person.name.eq("roach))
하지만 코틀린의 infix 를 이용한다면 다르게 풀어볼 수도 있을 것이다.
selectFrom person where person.name is "roach"
내 생각엔 Kotlin 에서 QueryDSL 을 쓰는건 계속해서 JPA + QueryDSL 을 써왔기때문이라고 생각하는데
Kotlin + Spring + JPA 를 이용한다면 QueryDSL 보다 infx 를 이용한 Kotlin 스러운 DSL 로 다들 옮기고 싶어하지 않을까 싶기도 하다.
왜냐면 위에 문법만 보더라도 아래 infix 를 이용한 DSL 이 좀 더 가독성이 좋다고 생각한다.
그래서 기존의 Java 진영에서 계속해서 사용하던 테스트 프레임워크 보다
Kotlin 의 infix 를 이용해 좀 이쁘고 표현력이 좋은 라이브러리를 원했다.
그래서 알아보던 도중에 Ko-test 라는 프레임워크를 알게 되었다.
일단 Kotest 를 보면 아래와 같은 문법으로 작성한다 좀 더 사람이 이해하기 쉬운 문법으로 변화했다고 생각한다.
class MyTests : StringSpec({
"length should return size of string" {
"hello".length shouldBe 5
}
"startsWith should test for a prefix" {
"world" should startWith("wor")
}
})
일단 백문이 불여일타라고 빠르게 코드로 작성해보도록 하자.
Gradle 설정하기
Ko-Test 는 Junit Plugin 을 사용하므로 반드시 build.gradle.kt 에 아래와 같은 내용을 적어주어야 한다.
tasks.withType<Test> {
useJUnitPlatform()
}
이제 dependencies 를 정의해서 다운로드 받아보자.
testImplementation("io.kotest:kotest-runner-junit5:$version")
testImplementation("io.kotest:kotest-assertions-core:$version")
그리고 나서 아래와 같은 클래스를 만들어 한번 테스트 해보자.
class MyFirstTestClass : FunSpec({
test("my first test") {
1 + 2 shouldBe 3
}
})
대충 FunSpec 코드를 까보면 반드시 Unit 형태로 넘겨야 한다.
abstract class FunSpec(body: FunSpec.() -> Unit = {}) : DslDrivenSpec(), FunSpecRootScope {
init {
body()
}
}
한번 테스트를 실행시켜보자.
위와 같이 테스트가 잘 동작함을 알 수 있다.
개인적으로 Ko-test 를 좋아하는 이유는 예전에 JavaScript 진영의 Jest 를 썼을때 Describe Spec 구조를 되게 좋아했는데, 이게 가능해서 Ko-test 를 이용한다면 이를 좀 이용해봐야 겠다는 생각이 들었다.
class NestedTestExample : DescribeSpec({
describe("an outer test") {
it("an inner test") {
1 + 2 shouldBe 3
}
it("an inner test do") {
3 + 4 shouldBe 7
}
}
})
LifeCycle Callback
우리가 기존에 사용하던 테스트 프레임워크에서는 모두 BeforeEach / AfterEach 와 같은
Setting Up / Tear Down 기능들을 주로 제공하는데 Ko-test 에서도 역시 제공한다.
class LifeCycleCallback : FunSpec({
beforeEach {
println("Hello from $it")
}
test("sam should be a three letter name") {
"sam".shouldHaveLength(3)
}
afterEach {
println("Goodbye from $it")
}
})
되게 마음에 드는 점은 Callback 을 별도의 Class 로 분리해서 재사용성을 높여주는 것인데
이건 약간 React 의 Hooks 느낌이 들었다.
val resetDatabase: BeforeTest = {
println("reset database!!!")
}
class LifeCycleCallback : FunSpec({
beforeTest(resetDatabase)
beforeEach {
println("Hello from $it")
}
test("sam should be a three letter name") {
"sam".shouldHaveLength(3)
}
afterEach {
println("Goodbye from $it")
}
})
위처럼 BeforeTest 에 대한 부분이 계속해서 재사용이 가능한데
실제로 많은 boilerPlate 에 대한 관리를 잘 할 수 있겠다는 생각이 들었다.
다른 좋은 점도 더 많은데 나중에 Spring 에 적용해보면서 더 잘 적어보려고 한다.
'Kotlin' 카테고리의 다른 글
Kotlin DSL (0) | 2022.03.28 |
---|---|
Kotlin 의 확장함수가 좋은 이유 (0) | 2022.03.18 |
Kotlin 에서 Null 을 다루기 (0) | 2022.01.18 |
[Kotlin Spring] Logger 를 간편하게 (0) | 2021.12.14 |
[Mac] Kotlin 설치 및 CLI 에서 사용하기 (0) | 2021.12.06 |