question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Support step definitions written in Kotlin

See original GitHub issue

Summary

Cucumber currently supports Kotlin by using Java Annotations or Lambda based step definitions. This works because Java and Kotlin can interoperate. This does however fails to leverage some of the advantages of Kotlin (#1554, #1520). So a Kotlin backend would be most useful.

Update:

The approach describe below will not work in the future because we are aiming for a design where step definitions must be discoverable without instantiating the glue class. For this reason we are deprecating cucumber-java8 (https://github.com/cucumber/cucumber-jvm/issues/2174) and this design was patterned on that. As a replacement for cucumber-java8 we are considering cucumber-lambda (https://github.com/cucumber/cucumber-jvm/issues/2279). This may or may not make this ticket obsolete.

Goals

  1. Implement a Kotlin backend that supports step definitions, hooks, data table and parameter type declaration.

  2. Avoid the duplication of method name and step definition common when using annotation based step definitions. Example:

Given("a step definition")
public void given_a_step_definition() {}
  1. Use Kotlin primitives and types everywhere. This should include Cucumber expressions, parameter types, data table types, DataTable and DocString.

  2. Generate Kotlin step definitions

Step definitions

class StepDefinitions(belly: Belly) : En {
   init {
     Given("there are {int} cukes in my belly"){ cukes : Int -> 
         belly.setCukes(cukes)
     }

      When("^I eat ([0-9]) cukes$") { cukes : Int ->
         belly.addCukes(cukes)
     }
   }
}

En would be implement as a class with members for various keywords and other glue (see java8 equivalent).

interface Test {

    fun Given(expression: String, f: Function<Unit>) {
        //Register with thread local as in Java 8
    }
    
    ... other key words
}

Note that Function has subtypes that support up to up to 22 parameters. After that it becomes a varg and we can no longer user f.javaClass.methods[1].genericParameterTypes to work out what the the types were. As a result Cucumber won’t be able to convert arguments automatically when using regular rather then Cucumber expressions. This is a reasonable drawback for wanting to use 22+ parameters.

Steps to take

  1. Implement a KotlinBackendProviderService and KotlinBackend and KotlinStepDefinition for a single annotation. For examples see Java8** equivalents in the java8 module.
  2. Experiment to see how the interop works
  3. Implement everything else (sorry about the draw-the-rest-of-the-owl, I don’t know what or how yet).

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:1
  • Comments:20 (9 by maintainers)

github_iconTop GitHub Comments

2reactions
mpkorstanjecommented, Dec 10, 2021

This looks promising:

package io.cucumber.kotlin

import kotlin.reflect.KClass

class World {

    fun hello() {
        println("Hello world!")
    }

    fun hello(number: Int) {
        println("Hello world ${number}!")
    }
}

val stepDefinitions = define {
    using<World> {
        step("hello world") {
            hello();
        }
        step("hello again world {int}") { number: Int ->
            hello(number)
        }
    }
}

The plumbing:


fun define(define: StepDefinitions.() -> Unit): StepDefinitions {
    val stepDefinitions = StepDefinitions()
    stepDefinitions.define()
    return stepDefinitions;
}

class StepDefinitions {
    val stepDefinitions: MutableList<Any> = mutableListOf()
    inline fun <reified T : Any> using(define: Using<T>.() -> Unit) {
        val context = Using(T::class)
        context.define()
        stepDefinitions.addAll(context.stepDefinitions);
    }
}

class Using<T : Any>(val kls: KClass<T>) {
    val stepDefinitions: MutableList<Any> = mutableListOf()
    inline fun step(expression: String, noinline implementation: T.() -> Unit) {
        stepDefinitions.add(StepDefinition00(expression, implementation, kls))
    }

    inline fun <reified V1 : Any> step(expression: String, noinline implementation: T.(a: V1) -> Unit) {
        stepDefinitions.add(StepDefinition01(expression, implementation, kls, V1::class))
    }
}

data class StepDefinition00<T : Any>(
    val expression: String,
    val implementation: T.() -> Unit,
    val executionContextType: KClass<T>
)

data class StepDefinition01<T : Any, V1 : Any>(
    val expression: String,
    val implementation: T.(V1) -> Unit,
    val executionContextType: KClass<T>,
    val expressionArgType01: KClass<V1>
)

fun main() {
    stepDefinitions.stepDefinitions.forEach { println(it) }
}

1reaction
mpkorstanjecommented, Jul 17, 2021

Is it as simple as saying that if the description is not provided, the name of the method is used as the description?

Unfortunately not. See: https://github.com/cucumber/cucumber-jvm/issues/1829#issuecomment-558640666

Read more comments on GitHub >

github_iconTop Results From Across the Web

Kotlin - Cucumber Documentation
There is no native Kotlin implementation of Cucumber, but you can use Cucumber-JVM to write Cucumber tests in Kotlin.
Read more >
Create step definitions | IntelliJ IDEA Documentation - JetBrains
In Cucumber, step definitions should be stored in a named package under Test Sources Root. ... A package for step definitions in the...
Read more >
IDEA plugin "Cucumber for Kotlin"?
... supports Cucumber for Java, Groovy and Scala. Is there any plan to add a similar plugin for Kotlin? I tried to write...
Read more >
Kukumber — Getting started with Cucumber in Kotlin - Medium
Unfortunately the IntelliJ Cucumber plugin does not (yet) support a link between the steps in our feature file and our step definitions, ...
Read more >
A Practical Example of Cucumber's Step Definitions in Java
When writing Cucumber tests in Java, it's common to use the Step Definition class to keep the state within the multiple methods that...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found