Skip to content
/ ktest Public

kTest - integration/acceptance/system/whatever test oriented modular test framework in Kotlin

License

Notifications You must be signed in to change notification settings

saksmt/ktest

Repository files navigation

kTest

CircleCI Maven metadata URI

kTest is integration / acceptance / system / any other non-unit test oriented modular test framework in Kotlin. Inspired by kotlintest, specs2 and martians.

Usage

object MyTestSpecification : BehaviorSpec({
    given("some service") {
        `when`("executing it's API") {
            then("it should work correctly") {
                // test body...
            }
        }
    }
})
fun makeMyHttp(): List<Account> {
    return rest["my-backend"] {
        using(url) {
            agreements / param("id") / accounts 
        } execute {
            GET(pathParam(123), header("Accept", "application/json"))
        }
    }
}
fun loadAccounts(resourcePath: String) =
    resourcePath.loadAsJson { list(map<String, Any>()) }.also {
        println(it.dump()) // pretty-printing pseudo-logging
    }
fun compareMyJsons(expected: JsonNode, actual: JsonNode) {
    with(JsonNodeMatchers) {
        assertThat(actual, isIdenticalTo(expected).bySubtree {
            // comparing only meaningful nodes
            "accounts[*]" {
                + "id"
                + "accountNumber"
                
                "owner" {
                    + "id"
                    + "name"
                }
            }
        })
    }
}

Powered by JSONPath

fun allIdsNearAccountNumbers(jp: DocumentContext) =
    jp select "id" where {
        "accountNumber".exists()
    } castTo { list<Long>() }
fun getAccounts(activeOn: Date) =
    "my-database".db {
        select<Account>("""
           | SELECT * FROM accounts 
           | WHERE 
           |   close_date is NULL 
           |   OR close_date < :activeOn
        """.trimMargin()) {
            parameter("activeOn", activeOn)
        }.asList()
    }
object MyTest : SimpleSpec({
    suite("my service suite") {
        restTest(name = { "${it.method} accounts" }) {
            url { agreements / accounts }
        
            GET(queryParam("name", "%"))
            POST(body("name", "%")) { it / search }
            
            expect { response: DocumentContext ->
                // do some check
            }
        }
    }
})

Reporting powered by excellent Allure

object MyTest : AllureSpec({
    feature("some feature", metaInfo = {
        blocker()
    }) {
        story("true story", metaInfo = {
            issue("PROJ-111")
        }) {
            case("my case", metaInfo = {
                description("my description")
            }) {
                // test body...
            }
        }
    }
})

Putting it all together

object AccountByCustomerRestApiSpec : AllureSpec({
    beforeAll {
        rest["backend"] {
            using(url) {
                `internal` / caches
            } execute {
                DELETE(queryParam("force", "true"))
            }
        }
    }

    epic("Search") {
        feature("Account by customer search") {
            story("Single criteria search") {
                val testTable = table(
                    header("criteriaName", "criteriaValue", "expectedJsonName"),
                    row("billing", ">100", "richAccounts.json"),
                    row("region", "Central", "centralRegionAccounts.json"),
                    row("validTill", ">${LocalDate.now().format(DateTimeFormatter.ISO_DATE)}", "activeAccounts.json")
                )
                // should be generated right before test
                val myGeneratedCustomer: Customer = testData["customer.json"]
                
                forAll(testTable) { criteriaName, criteriaValue, expectedJsonName ->
                    val criteria = mapOf<String, String>(
                        criteriaName, criteriaValue
                    )
                    
                    restTest(name = { "Search account by \"$criteriaName\": ${it.method}" }, metaData = {
                        category<Complex>()
                        flaky()
                    }) {
                        url { customers / param("customerId") / accounts }
                        
                        GET(queryParams(criteria), pathParam("customerId", myGeneratedCustomer.id))
                        POST(body(criteria), pathParam("customerId", myGeneratedCustomer.id))
                        
                        expect { response: DocumentContext ->
                            with(DocumentContextMatchers) {
                                assertThat(response, matches(expectedJsonName.loadAsJsonPath()).afterRemovalOfSubtree {
                                    "account[].metaData" {
                                        + "date"
                                        + "IP"
                                    }
                                })
                            }
                        }
                    }
                }
            }
        }
    }
})

For more see docs and samples

Download

You can use dependency management

Gradle

compile 'run.smt.ktest:ktest-api'
compile 'run.smt.ktest:ktest-config'
compile 'run.smt.ktest:ktest-util'
compile 'run.smt.ktest:ktest-runner-junit4'
compile 'run.smt.ktest:ktest-allure'
compile 'run.smt.ktest:ktest-jackson'
compile 'run.smt.ktest:ktest-json-matchers'
compile 'run.smt.ktest:ktest-jsonpath'
compile 'run.smt.ktest:ktest-db'
compile 'run.smt.ktest:ktest-rest'
compile 'run.smt.ktest:ktest-resttest'

Maven

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-api</artifactId>
</dependency>

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-config</artifactId>
</dependency>

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-util</artifactId>
</dependency>

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-runner-junit4</artifactId>
</dependency>

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-allure</artifactId>
</dependency>

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-jackson</artifactId>
</dependency>

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-json-matchers</artifactId>
</dependency>

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-jsonpath</artifactId>
</dependency>

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-db</artifactId>
</dependency>

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-rest</artifactId>
</dependency>

<dependency>
    <groupId>run.smt.ktest</groupId>
    <artifactId>ktest-resttest</artifactId>
</dependency>

License

All source code is licensed under MIT license