728x90
반응형
Document
- Document: https://kotest.io/
- Github Reference Dockument: https://github.com/kotest/kotest/wiki/Reference-Doc
Introduce
- Kotest는 Kotlin을 위한 테스팅 도구
- Kotlin-Test에서 Kotest로 이름이 변경되었으며, 이름에서 알 수 있듯이 Kotlin언어로 작성된 코드를 테스트하기에 아주 용이
- Spock와 유사하게 간결한 코드와 다양한 유형의 테스트 방식을 지원
- Kotest는 multi-platform test framework로, 각 하위 프로젝트는 독립적으로 사용할 수 있습니다.
- Test Framework
- Assertion Library
- Property Testing
- 문서화가 잘되어있긴 하지만, 아직 도큐먼트나 참고할 레퍼런스들이 아직 다양하지 않은 것 같다. (2021년 8월 기준)
도입해보게된 배경
- 팀에서 주로 사용하는 Spock framework (Test with Spock framework) 와 Kotlin + Spring 조합 사용 시, Spring Component Annotation을 붙이지 않은 클래스의 mock 객체가 생성 되지 않는 문제가 발생 (실제 내부로직을 타고 들어감)
- 추측되는 원인
- 기본적으로 Kotlin 클래스의 경우, class에 별다른 키워드를 붙이지 않는 경우 final 클래스로 클래스가 생성
- Groovy의 경우, Mockito와 비슷한 형태를 띄고 있고 Mockito의 Mock 또한 final class를 mocking 할 수 없는 문제를 갖고 있어 발생한 문제로 보여짐
- class를 open class로 변경해서 mock 객체를 만드니 성공
- 추측되는 원인
- Kotlin 친화적인 테스팅 도구 서칭
How to use
Quick Start : 의존성 설정
JVM/Gradle
- 사용할 기능들에 해당하는 라이브러리만 가져다 사용하면 됩니다.
test {
maxHeapSize = "1024m"
useJUnitPlatform()
}
...
dependencies {
testImplementation("io.kotest:kotest-runner-junit5-jvm:$kotestVersion") //Test Framework
testImplementation("io.kotest:kotest-assertions-core:$kotestVersion") //Assertions Library
testImplementation("io.kotest:kotest-property:$kotestVersion") //Property Testing
testImplementation("io.kotest:kotest-extensions-spring:$kotestVersion") //Spring Extensions
}
IntelliJ Plugin
Kotest는 Jetbrains 플러그인 마켓플레이스(IntelliJ 내에서 검색)에서 사용할 수 있는 IntelliJ 플러그인을 제공합니다.
이 플러그인은 각 테스트에 대한 실행 아이콘, 테스트 탐색을 위한 도구 창, 중복 테스트 강조 표시, assertion intentions 등을 제공합니다.
Intellij 플러그인을 사용하기 위해서는 Kotest 4.2 이상이 필요합니다.
Testing Style (Spec)
- Kotest는 10가지 스타일의 테스트 레이아웃을 제공합니다. (일부는 다른 인기 있는 테스트 프레임워크에서 영감을 받아 제작)
- Kotest를 사용하려면 테스트 스타일 중 하나를 Extend하는 클래스 파일을 만들어 사용
- 그런 다음 init { } 블록 내에서 테스트 케이스를 만듬
- 스타일 간에 기능적 차이는 없음
- 모두 동일한 유형의 구성(스레드, 태그 등)을 허용
- 테스트를 구성하는 방법은 단순히 선호도의 문제
Example
String Spec (A Kotest original)
class MyTests : StringSpec({
"strings.length should return size of string" {
"hello".length shouldBe 5
}
})
Fun Spec (ScalaTest)
class MyTests : FunSpec({
test("String length should return the length of the string") {
"sammy".length shouldBe 5
"".length shouldBe 0
}
})
Behavior Spec (BDD Framework)
class MyTests : BehaviorSpec({
given("a broomstick") {
//given condition
`when`("I sit on it") {
//when condition
then("I should be able to fly") {
// test code
}
}
`when`("I throw it away") {
then("it should come back") {
// test code
}
}
}
})
Feature Spec (Cucumber)
class MyTests : FeatureSpec({
feature("the can of coke") {
scenario("should be fizzy when I shake it") {
// test here
}
scenario("and should be tasty") {
// test here
}
}
})
Annotation Spec (JUnit)
class AnnotationSpecExample : AnnotationSpec() {
@BeforeEach
fun beforeTest() {
println("Before each test")
}
@Test
fun test1() {
1 shouldBe 1
}
@Test
fun test2() {
3 shouldBe 3
}
}
Test Listeners
- 테스트 전/후, Spec 클래스의 모든 테스트 전/후, 전체 프로젝트 전/후 등과 같은 테스트 엔진 생명 주기에 어떤 세팅을 할 때 사용할 수 있도록 TestListener 인터페이스 제공
Function | 목적 |
beforeTest | 테스트 케이스가 실행되기 전에 매번 호출됩니다. 무시되는 테스트(아래의 Test Ignore 참고) 에서는 실행되지 않습니다. |
afterTest | 테스트 케이스가 실행된 후 매번 호출됩니다. 무시되는 테스트에서는 실행되지 않습니다. 테스트가 실패하더라도 실행됩니다. |
beforeSpec | 모든 beforeTest 함수가 호출되기 전에, Spec이 시작될 때마다 호출됩니다. |
afterSpec | 모든 afterTest 함수가 호출된 후 Spec이 완료될 때마다 호출됩니다. |
beforeSpecClass | 엔진이 실행할 Spec을 준비할 때 호출됩니다. Spec이 인스턴스화되는 횟수에 관계없이 한 번만 실행됩니다. |
afterSpecClass | 사양이 인스턴스화되는 횟수에 관계없이 사양에 대한 모든 테스트가 완료되면 호출됩니다. |
beforeProject | 테스트 엔진이 시작되는 즉시 호출됩니다. |
afterProject | 테스트 엔진이 완료되는 즉시 호출됩니다. |
afterDiscovery | 모든 Spec 클래스를 검색하고, beforeSpec 함수가 호출되기 전과 테스트 엔진에서 Spec을 인스턴스화하기 전에 호출됩니다. |
Example
class HelloTest : StringSpec({
lateinit var helloDaoMock: HelloDao
lateinit var helloService: HelloService
beforeTest {
helloDaoMock = mockk()
helloService = HelloService(helloDaoMock)
}
...
}
Mocking
- Kotest 자체에는 모의 기능이 없습니다.
- 하지만 Mock 라이브러리를 쉽게 플러그인하여 사용할 수 있습니다.
- 예를 들어 mockk 을 사용하여 다음과 같이 작성할 수 있습니다.
- mockk: https://mockk.io/
testImplementation "io.mockk:mockk:{version}"
Example
class MyTest : FunSpec({
val repository = mockk<MyRepository>()
val target = MyService(repository)
test("Saves to repository") {
every { repository.save(any()) } just Runs
target.save(MyDataClass("a"))
verify(exactly = 1) { repository.save(MyDataClass("a")) }
}
test("Saves to repository as well") {
every { repository.save(any()) } just Runs
target.save(MyDataClass("a"))
verify(exactly = 1) { repository.save(MyDataClass("a")) }
}
})
Matchers
- Matchers는 Variable 또는 Function이 특정 값을 가져야 한다고 Assert 할 때 사용
- Kotest에는 100개 이상의 내장 Matcher가 있습니다.
Matcher의 Two Styles
- Extension functions
- Infix functions
Example
class MatcherTest: StringSpec({
"Simple Test"{
//haveLength(): length 체크
"Lorem ipsum dolor" shouldBe haveLength(17)
//include(): 파라미터가 포함되어있는지 체크
"Lorem ipsum dolor" should include("ipsum")
//endWith(): 파라미터가 끝에 포함되는지 체크
"Lorem ipsum dolor" should endWith("lor")
//match(): 파라미터가 매칭되는지 체크
"Lorem ipsum dolor" should match("Hello World")
//shouldBeLowerCase(): 소문자로 작성되어있는지 체크
"Lorem ipsum dolor".shouldBeLowerCase()
val list = emptyList<String>()
val list2 = listOf("aaa", "bbb", "ccc")
val map = mapOf<String, String>(Pair("aa", "11"))
//beEmpty(): 원소가 비어있는지 체크
list should beEmpty()
//sorted<T>(): 해당 자료형이 정렬되어있는지 체크
list2 shouldBe sorted<String>()
//contain(): 해당 원소가 포함되어있는지 체크
map should contain("aa", "11")
//haveKey(): 해당 key가 포함되어있는지 체크
map should haveKey("aa")
//haveValue(): 해당 value가 포함되었는지 체크
map should haveValue("11")
}
})
Kotest Extension
Kotest는 다른 많은 라이브러리 및 프레임워크와 통합됩니다. 일부는 Kotest 팀에서 제공하고 나머지는 3rd parties에서 유지 관리 및 호스팅합니다.
JUnit vs Spock vs Kotest
Ref. https://veluxer62.github.io/explanation/comparing-testing-library-for-kotlin/
728x90
반응형
'개발 > Java, Kotlin' 카테고리의 다른 글
[Effective Kotlin] Item 24: Consider variance for generic types (0) | 2021.09.09 |
---|---|
[Effective Kotlin] Item 17: Consider naming arguments (1) | 2021.08.25 |
[Effective Kotlin] Item 10: Write unit tests (0) | 2021.08.12 |
가비지컬렉션 (Garbage Collection) (0) | 2020.12.29 |
JVM (Java Virtual Machine) (0) | 2020.12.28 |
댓글