Alkemy is a browser automation framework written in Kotlin and based on Selenium and Kotest. The objective is to provide more fluent definitions for Selenium tests, using a functional style and Kotlin extension functions. Alkemy currently supports Chrome and Firefox browsers.
- Extension functions and assertions
- Custom test selector
- Reports
- Run multiple browsers in parallel
- Spring Boot Module. Contributed by @daniel-shuy.
- Reference documentation
- Various test examples
- Full working project setup
- Spring boot integration
- API Docs - Alkemy core
- API Docs - Alkemy Spring
testImplementation "io.resoluteworks:alkemy:${alkemyVersion}"
class MyTest : StringSpec({
val context = defaultAlkemyContext()
A set of extensions functions can be used against String
to perform lookups and assertions.
class MyTest : StringSpec({
val context = defaultAlkemyContext()
"string selectors and assertions" {
// To use String extensions the context.apply{} construct is required
context.apply {
get("/dynamic_controls")
val form = "#input-example"
"$form button".shouldHaveText("Enable")
.shouldBeEnabled()
.click()
.shouldBeDisabled()
"$form #loading".shouldBeVisible()
"$form input[type='text']".shouldBeEnabled(maxWaitSeconds = 10)
}
}
Similar extensions are available for WebDriver
and WebElement
, including
helper methods like fillForm
, typeIn
and assertions like shouldBeVisible
, shoulHaveClass
, etc.
"login with fillForm" {
context.get("/login")
.fillForm(
"username" to "tomsmith",
"password" to "SuperSecretPassword!"
)
.submit() shouldHaveText "Welcome to the Secure Area"
}
Alkemy can use a globally defined data-*
HTML attribute to lookup elements.
Read more about this here.
<body>
<div data-test-selector="content-div">Secure area</div>
<div>Footer</div>
</body>
byTestSelector("content-div").text shouldBe "Secure area"
Or
"[${testSelector}=content-div]".text shouldBe "Secure area"
Please note this is only available within a context.apply{}
block.
Alkemy also provides a very basic framework for Page Object Model approaches. This includes all the extensions and
assertions available for WebDriver
.
class LoginPage(context: AlkemyContext) : Page(context, "/login") {
fun login(username: String, password: String): SecurePage {
fillForm("username" to username, "password" to password)
.submit()
return next<SecurePage>()
}
}
class SecurePage(context: AlkemyContext) : Page(context, "/secure")
"login with page object model" {
val securePage = context
.goTo<LoginPage>()
.login("tomsmith", "SuperSecretPassword!")
securePage shouldHaveText "Welcome to the Secure Area"
}
Any Kotest assertions can be used natively in combination with the Alkemy or Selenium objects.
"using native Kotest assertions" {
val driver = context.get("/login")
driver.findElements("input") shouldHaveSize 2
driver.find("h2").text shouldContain "Login Page"
}
Example to customize the Alkemy config for a Spec:
class MyTest : StringSpec({
val context = customAlkemyContext(
AlkemyConfig(baseUrl = baseUrl),
)
})
To customize the default Alkemy config for a Spec, use AlkemyConfig
's copy constructor to overwrite any properties,
e.g.
class MyTest : StringSpec({
val context = customAlkemyContext(
AlkemyConfig.fromSystemProperties().copy(
browser = Browser.FIREFOX,
headless = true,
windowWidth = 800,
windowHeight = 600,
),
)
})
If Kotest auto scan is disabled, to enable
pooling, you will need to manually load the AlkemyWebDriverPoolExtension
extension in your Project config, e.g.
class ProjectConfig : AbstractProjectConfig() {
override fun extensions() = listOf(AlkemyWebDriverPoolExtension)
}
See Documentation for further information.