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.

Consider cucumber-java-lambda as a replacement for cucumber-java8

See original GitHub issue

cucumber-java8 allows step definitions to be defined as lambda’s. This is really nice because it removes the need to type your step definition twice as you would with cucumber-java. So there is a good reason to use lambda’s to define step definitions.

Compare:

Given("A gherkin and a zukini", () -> { 

});

@Given("A gherkin and a zukini")
public void a_gherkin_and_zukini(){

}

Unfortunately with cucumber-java8 lambda’s must be defined in the constructor of a step definition class. As a result we can not know which step definitions are defined until a Cucumber scenario has started and all world objects are instantiated. This makes it impossible to discover, cache and validate step definitions up front, preventing us from making Cucumber more efficient (#2035).

public class StepDefinitions {
     public StepDefinitions(){
        Given("A gherkin and a zukini", () -> { 

       });
     }
}

Additionally Cucumber uses typetools to determine the type of lambda parameters. This requires the use of of Unsafe to fish in the constant pool. This is a non-trivial process and Cucumber currently uses typetools to facilitate this. However because this fundamentally depends on unsafe operation it is not guaranteed to work in the long run.

Requested solution

  1. Implement cucumber-lambda as an alternative for cucumber-java8 that uses a DSL to build step definitions. Because this DSL is created in a static field it can be discovered in the same way cuucmber-java discovers step definitions and avoids the issues of cucumber-java8.
public class CucumberLambdaStepDefinitions {

    @Glue
    public static CucumberLambda glue = CucumberLambda
        .using(World.class)
        .step("A gherkin and a zukini", (World world) -> () -> { 
             world.setGherkins(1);
             world.setZukinis(1);
        })
        .step("{int} gherkin(s) and {int} zukini(s)", (World world) -> (int gherkins, int zukinis) -> {
             world.setGherkins(gherkins);
             world.setZukinis(zukinis);
        });

       // repeat for hooks, parameter types and data table types, ect
       // hooks should be named beforeAll, beforeEach, beforeStep.
  1. Avoid the use of typetools where possible by specifying all parameter types

  2. The World object is created using DI as usual. Consider the possibility of defining steps/hooks using multiple objects.

CucumberLambda
    .using(GherkinPatch.class, ZukiniPatch.class)
    .step("A gherkin and a zukini", (gherkinPatch, zukiniPatch) -> () -> { 
        // tend to the vegetable garden here
    });

Out of scope

  1. Generate localized vairations of the DSL that use Given/When/Then.
    @Glue
    public static CucumberLambda glue = io.cucumber.lambda.en.CucumberLambda
        .using(World.class)
        .given("A gherkin and a zukini", (World world) -> () -> { 
             world.setGherkins(1);
             world.setZukinis(1);
        })
        .when("{int} gherkin(s) and {int} zukini(s)", (World world) -> (int gherkins, int zukinis) -> {
             world.setGherkins(gherkins);
             world.setZukinis(zukinis);
        });
        // ect.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:3
  • Comments:29 (23 by maintainers)

github_iconTop GitHub Comments

2reactions
mpkorstanjecommented, Apr 4, 2021

I don’t understand a word of what you are saying.

1reaction
mpkorstanjecommented, Apr 25, 2022

Some more sketches in https://github.com/mpkorstanje/cucumber-lambda-proposals

There are 4 options to consider:

package io.cucumber;

import io.cucumber.lambda.Glue;
import io.cucumber.lambda.StepDefinitions;
import io.cucumber.lambda.context.GherkinPatch;
import io.cucumber.lambda.context.World;
import io.cucumber.lambda.context.ZukiniPatch;

import static io.cucumber.lambda.StepDefinitions.using;
import static io.cucumber.lambda.StepDefinitions.with;

@SuppressWarnings("unused")
public class ApiSketch {

    /**
     * Advantages:
     * 1. Clear visual separation between context and step definition.
     * 2. Lambdas provide natural formatting breaks
     * 3. Allows method extraction.
     * 4. Kotlin equivalent can use "Function literals with receiver"
     * Disadvantages:
     * 1. Visually a bit verbose
     */
    @Glue
    public static StepDefinitions doubleLambda = using(World.class)
            .step("{int} gherkin(s) and {int} zukini(s)", (World world) -> (Integer gherkins, Integer zukinis) -> {
                world.setGherkins(gherkins);
                world.setZukinis(zukinis);
            });

    @Glue
    public static StepDefinitions doubleLambdaWithMethodReference = using(World.class)
            .step("{int} gherkin(s) and {int} zukini(s)", (World world) -> world::setGherkinsAndZukinis);

    /**
     * Advantages:
     * 1. Delays the need for dependency injection
     * 2. Would be different from Kotlin equivalent
     * Disadvantages:
     * 1. Visually a very verbose
     */
    @Glue
    public static StepDefinitions doubleLambdaWithMultiContexts = using(GherkinPatch.class, ZukiniPatch.class)
            .step("{int} gherkin(s) and {int} zukini(s)",
                    (GherkinPatch gherkinPatch, ZukiniPatch zukiniPatch) -> (Integer gherkins, Integer zukinis) -> {
                        gherkinPatch.setGherkins(gherkins);
                        zukiniPatch.setZukinis(zukinis);
                    });

    /**
     * Advantages:
     * 1. Visually short
     * Disadvantages:
     * 1. No separation between context and step definition function
     * 2. No method extraction
     */
    @Glue
    public static StepDefinitions singleLambda = with(World.class)
            .step("{int} gherkin(s) and {int} zukini(s)", (World world, Integer gherkins, Integer zukinis) -> {
                world.setGherkins(gherkins);
                world.setZukinis(zukinis);
            });

    @Glue
    public static StepDefinitions singleLambdaWithMultipleContext = with(GherkinPatch.class, ZukiniPatch.class)
            .step("{int} gherkin(s) and {int} zukini(s)",
                    (GherkinPatch gherkinPatch, ZukiniPatch zukiniPatch, Integer gherkins, Integer zukinis) -> {
                        gherkinPatch.setGherkins(gherkins);
                        zukiniPatch.setZukinis(zukinis);
                    });

}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Cucumber Java 8 Support - Baeldung
In this quick tutorial, we'll learn how to use Java 8 lambda expressions with Cucumber. 2. Maven Configuration.
Read more >
Automate Cucumber-JVM upgrades with OpenRewrite
Some work has been put into a possible replacement in the form of cucumber-java-lambda , but in the meantime users are recommended not...
Read more >
Cucumber, Java8: find usage of step defined by lambda ...
I believe this is always the next release as one line up it states Fixed in builds No Fixed in build . As...
Read more >
Part 11 - New feature in Cucumber-Java 8 - YouTube
In this video we will discuss about the new feature of Cucumber-Java 8 and how we can leverage the power of Lambda expression...
Read more >
Step Definitions - Cucumber Documentation
package com.example; import io.cucumber.java8. ... You can use this as a starting point for new step definitions. Consider this Gherkin step:
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