Skip to content

Kotest Specs

https://kotest.io/docs/framework/testing-styles.html

Kotest는 10가지 다른 스타일의 테스트 레이아웃을 제공한다. 그중 일부는, 다른 테스트 프레임워크와 비슷한 구조로 만들어지기도 했다.

스타일 간에 기능적인 차이는 없다. 스레드, 태그 등 모두 동일한 유형의 구성을 허용하지만, 이는 단순히 테스트를 구성하는 방법에 대한 선호도의 문제이다.

코테스트를 사용하려면 테스트 스타일 중 하나를 확장하는 클래스 파일을 만들어야 한다. 그런 다음 원하는 Spec (추상)클래스를 상속받고 init {} 블록 안에서 테스트 케이스를 작성하면 된다.

테스트 스타일유사한 테스트 프레임워크
Fun SpecScalaTest
String SpecA Kotest original
Should SpecA Kotest original
Describe SpecJavascript frameworks과 RSpec
Behavior SpecBDD frameworks
Word SpecScalaTest
Free SpecScalaTest
Feature SpecCucumber
Expect SpecA Kotest original
Annotation SpecJUnit

Fun Spec

FunSpec 테스트는 String으로 이름을 지정한다음 테스트 자체를 람다로 호출하여 테스트를 생성할 수 있다.

내용은 별다른 형식 없이 그냥 자유롭게 작성하고, 이름만 저런 식으로 지정해줄 수 있는 형식인 것 같다.

class MyTests : FunSpec({
test("String length should return the length of the string") {
"sammy".length shouldBe 5
"".length shouldBe 0
}
})

String Spec

Fun Spec과 똑같은 방식이지만, test()로 묶어줄 필요 없이 바로 string과 람다를 써서 작성하는 Spec이다.

class MyTests : StringSpec({
"strings.length should return size of string" {
"hello".length shouldBe 5
}
})

추가설정은 이런 식으로 할 수 있다.

class MyTests : StringSpec({
"strings.length should return size of string".config(enabled = false, invocations = 3) {
"hello".length shouldBe 5
}
})

Should Spec

ShouldSpec은 funSpec에서 testshould로 바뀐 것이다.

class MyTests : ShouldSpec({
should("return the length of the string") {
"sammy".length shouldBe 5
"".length shouldBe 0
}
})

Describe Spec

Describe Spec은 DCI 패턴으로 테스트코드를 작성할 수 있는 Spec이다.

describe에는 도메인 혹은 함수명, 어떻게 되는지를 각각 적으면 된다.

class MyTests : DescribeSpec({
describe("score") {
it("start as zero") {
// test here
}
describe("with a strike") {
it("adds ten") {
// test here
}
it("carries strike to the next frame") {
// test here
}
}
describe("for the opposite team") {
it("Should negate one score") {
// test here
}
}
}
})

Docs에 예시는 위와 같이 되어있지만, Describe Spec으로는 보통 중간에 context를 껴서 이런식으로 작성하는 경우가 많다.

JUnit5로 DCI 패턴을 쓰려면 이렇게 해야하는데, Kotest를 쓰면 중첩테스트가 되니까 그냥 할 수 있다.

describe("PASSWORD_EXP") {
context("영어 대소문자, 영어, 특수문자가 포함된 8-30자의 문자열이 주어지면"){
val string = "Password!1"
it("참이 반환된다") {
Pattern.matches(RegexUtil.PASSWORD_EXP, string) shouldBe true
}
}
}

Behavior Spec

우리가 보통 아는 given, when, then 패턴이다.

하지만 when이 Kotlin의 예약어이기 때문에 Behavior Spec을 사용할떄 backtick(`)으로 감싸서 사용해야한다. 그게 마음에 들지 않는 경우 Given, When, Then을 쓸 수도 있다.

class MyTests : BehaviorSpec({
given("a broomstick") {
`when`("I sit on it") {
then("I should be able to fly") {
// test code
}
}
`when`("I throw it away") {
then("it should come back") {
// test code
}
}
}
})

Word Spec

WordSpec은 context 문자열 뒤에 테스트를 키워드 should를 사용하여 테스트를 작성한다.

class MyTests : WordSpec({
"String.length" should {
"return the length of the string" {
"sammy".length shouldBe 5
"".length shouldBe 0
}
}
})

또한 다른 수준의 중첩을 추가할 수 있는 When 키워드를 지원한다.

여기에서도 역시 when은 backtick(`) 또는 대문자 변형을 사용해야 한다.

class MyTests : WordSpec({
"Hello" When {
"asked for length" should {
"return 5" {
"Hello".length shouldBe 5
}
}
"appended to Bob" should {
"return Hello Bob" {
"Hello " + "Bob" shouldBe "Hello Bob"
}
}
}
})

Free Spec

Free Spec에서는 키워드 -(빼기)를 사용하여 임의의 깊이 수준을 중첩할 수 있다.

class MyTests : FreeSpec({
"String.length" - {
"should return the length of the string" {
"sammy".length shouldBe 5
"".length shouldBe 0
}
}
"containers can be nested as deep as you want" - {
"and so we nest another container" - {
"yet another container" - {
"finally a real test" {
1 + 1 shouldBe 2
}
}
}
}
})

Feature Spec

FeatureSpec은 feature, scenario를 사용한다. (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
}
}
})

Expect Spec

ExpectSpec은 FunSpec, ShouldSpec과 비슷하며, expect 키워드를 사용한다.

class MyTests : ExpectSpec({
expect("my test") {
// test here
}
})

context 블록에 중첩하여 작성할 수도 있다.

class MyTests : ExpectSpec({
context("a calculator") {
expect("simple addition") {
// test here
}
expect("integer overflow") {
// test here
}
}
})

Annotation Spec

JUnit과 동일한 방식이다. 별다른 이점은 없지만, 기존 형식을 그대로 가져올 수 있어 더 빠른 마이그레이션이 가능하다.

class AnnotationSpecExample : AnnotationSpec () {
@BeforeEach fun beforeTest () {
println ( " 각 테스트 전 " )
}
@Test fun test1 () {
1 shouldBe 1
}
@Test fun test2 () {
3 shouldBe 3
}
}