Kola
Kola background, Kola a consumer driver tester framework
Previous summary of What’s Kola, The Kola framework and tools are designed to provide a collaborative environment for all relevant stakeholders to comprehend and contribute to the testing process.
Kola is built upon industry-proven practices and methodologies, combining the convenience of engineering practices with the rigor of well-defined processes. Kola’s approach is primarily inspired by three mainstream testing philosophies.
In engineering practices, Kola has drawn significant references and inspirations from its predecessors like SmartBear’s PactFlow, Spring cloud Contract, Spock, and Karate.
Not to mention, Kola also builds upon the foundational testing frameworks such as JUnit5, TestNG, and AssertJ, which are cornerstones of the testing community.
Kola has consistently adhered to the principles of software development, striking a balance between learning curve, engineering practices, and team collaboration, to make the entire testing process more seamless and user-friendly.
Kola make your test life happier and colourful
The choice of a BDD (Behavior-Driven Development) style as the primary approach in Kola is intentional. While Kola does not utilize mainstream BDD frameworks like Cucumber as its foundation, the BDD style of expression is particularly well-suited for Kola’s purposes.
The BDD approach provides an excellent balance between the structured nature of the programming world and the easily understandable language for non-technical stakeholders. This makes it an ideal medium for expressing user stories and use cases, bridging the gap between the program’s behavior and the users’ requirements.
By adopting the BDD style, Kola aims to facilitate seamless communication and collaboration among all stakeholders, ensuring that the testing process aligns with the user’s perspective and the program’s intended behavior.
Feature: Explaining ApiHug
In order to gain an understanding of the ApiHug testing system
As a non-programmer
I want to have an overview of ApiHug that is understandable by non-geeks
Scenario: A worker seeks an overview of ApiHug
Given I have a coworker who knows a lot about ApiHug
When I ask my coworker to give an overview of how ApiHug works
And I listen to their explanation
Then I should have a basic understanding of ApiHug
That’s a great point about Kola’s approach to BDD. The standard BDD definition format is largely retained in Kola, except for the And
step being omitted.
This is because Kola’s typical use case involves verifying API requests and responses, where the And
step would essentially be the act of making the request, and the Then
step would directly validate the response.
Kola utilizes the hope.kola.contract.Feature
import, which allows for the use of Groovy
syntax in defining the protocols and test cases. This Groovy-based approach helps to streamline and enhance the overall experience of writing test cases.
👍 tip about the potential IDE recognition issue for the Feature Groovy files. Reloading the Gradle project in the IDE can often resolve this and ensure the IDE properly recognizes and supports the Kola-specific Groovy syntax.
A Background allows you to add some context to the scenarios that follow it. It can contain one or more Given steps, which are run before each scenario, but after any Before hooks.
The purpose of the Feature keyword is to provide a high-level description of a software feature, and to group related scenarios.
a written description of your product’s behavior from one or more users’ perspectives
Given steps are used to describe the initial context of the system - the scene of the scenario. It is typically something that happened in the past.
In the context of ApiHug, the Given
step represents the API environment, which can be imported from either ApiHug or a standalone API environment.
This is the most direct way:
Given {
api("UserService", "Login")
}
UserService
For the ApiHug service, it is best to import using the full package path, as this will be validated during compilation. Relative paths can also be used, but you must ensure there are no duplicates. The IDE (e.g. IDEA) provides tools to automate this process.Login
Method name, must be included in this service’s definitionDirectly using the APIs in the context of ApiHug can provide richer context, including definitions for the request, response, path, parameters, and validation rules.
supply request body pre-mock, no need to write the body from very begin manually.
This is kola recommend way!
If it’s a third-party API for which we don’t have the metadata, we can only define it manually.
Given {
get("https://github.com")
}
When steps are used to describe an event, or an action. This can be a person interacting with the system, or it can be an event triggered by another system.
In the definition of ApiHug, the When
step is used to assemble the context of the request.
method | usage |
---|---|
json | raw json |
body | update json body according to json path |
multipart | attachment files |
cookies | cookie info |
headers | header info |
queries | query parameters |
paths | path parameters |
JSON definition:
json("""
{
"name": "jake",
"age": 18,
"address": {
"country": "$V('key')",
"zip": 200021
}
}
""")
$V('key')
, inject from environment context through the expression language, this sample will pick key
value from the context;body manipulate:
If the request definition within the json
method is still not sufficient to meet the requirements, you can further modify the request by using JSON path expressions in the body section to update and override the original values.
body {
set('name', "same")
set('student.name', "blue")
set('student.age', 22)
set('student.weight', 123.3d)
set('student.friends', "jake", "blue", "yellow")
fromGlobal("student.country", "country")
}
Eventually assemble a suitable request body.
multipart {
file{
fromClassPath("hello.txt")
}
}
cookies {
//Possible Bear + {jwt}
fromGlobal(authorization(), "jwt")
}
queries {
pageable {
page(0)
size(12)
}
fromGlobal("userName", "jake")
}
paths {
fromGlobal("user-id", "userId")
}
Then steps are used to describe an expected outcome, or result.
Then {
isOk()
status 200
}
stringAssert("user.address.zipCode", {
isEqualTo("jake")
isBase64()
isAlphabetic()
startsWithIgnoringCase("json")
})
bigDecimalAssert("user.salary", {
isCloseTo(new BigDecimal("1112.22"), Offset.offset(12))
isGreaterThanOrEqualTo(new BigDecimal("231312"))
})
booleanAssert("user.live", {
isTrue()
})
postScript {
{
headSet("age", 1234)
globalSet("same", "blue")
}
}
Consumer Driven Contracts, What’s Kola’s domain language looks like?
How it easy to understand and get hand in. you will love it.
Default Extension, lifecycle management.
Use DSL to define environment also {WIRE_MODULE}/src/test/resources/config/kola.groovy
:
-Dtags=qa,dev
import com.test.bigger.example.Student
import static hope.kola.contract.Configuration.*
var big = 1123
[
common {
baseURI("https://qa.example.com")
port(9527)
p("date", ofDate("2022-12-12"))
rest {
log {
enablePrettyPrinting()
logBodyDetailIfValidationFails()
}
}
},
env("qa", {
baseURI("https://qa.example.com")
port(big)
}),
env("prod", {
baseURI("https://prod.example.com")
p("date", nowDayOfMonth())
p("bigStudent", new Student().setName("jake").setAge(19))
rest {
closeIdleConnectionsAfterEachResponse(12l, second())
log {
logAllDetailIfValidationFails()
}
}
})
]
import hope.kola.contract.Feature
import org.assertj.core.data.Offset
Feature.make {
priority 100
name("Customer login place order and check balance logic")
description("""Never judge the boss, as you may the real fool""")
Scenario "001 Try login ", {
preScript {
//Define prepare logic here
}
Given {
api("UserService", "Login")
}
When {
json("""
""")
body {
set('name', "same")
set('student.name', "blue")
set('student.age', 22)
set('student.weight', 123.3d)
set('student.friends', "jake", "blue", "yellow")
fromGlobal("student.country", "country")
}
queries {
pageable {
page(0)
size(12)
}
fromGlobal("userName", "jake")
}
paths {
fromGlobal("user-id", "userId")
}
}
And {
stringAssert("user.address.zipCode", {
isEqualTo("jake")
isBase64()
isAlphabetic()
startsWithIgnoringCase("json")
})
bigDecimalAssert("user.salary", {
isCloseTo(new BigDecimal("1112.22"), Offset.offset(12))
isGreaterThanOrEqualTo(new BigDecimal("231312"))
})
booleanAssert("user.live", {
isTrue()
})
}
postScript {
{
headSet("age", 1234)
globalSet("same", "blue")
}
}
}
Scenario "002 Place a order", {}
Scenario "003 Check balance", {}
}