Skip to content

Latest commit

 

History

History
221 lines (163 loc) · 7.35 KB

v5.1.0-v5.7.0.md

File metadata and controls

221 lines (163 loc) · 7.35 KB

Cucumber-JVM v5.1.0-v5.7.0

In the last three months we picked up the pace and managed to do a new release nearly every week. With v6-RC1 and v5.7.0 released now would be a good time to look back and see what is still relatively new in v5.

As always the full changelog can be in its usual place.

v5.2.0: Redefine build in data table types

In v5.0.0 empty cells in a data table would be transformed into null rather than the empty string. By using replaceWithEmptyString = "[blank]" on a datatable type an empty string can be explicitly inserted.

However, this did not work for when converting to build in types such as String and Object. It is now possible to redefine build in types.

This enables the following:

Feature: Whitespace
  Scenario: Whitespace in a table
   Given a blank value
     | key | value   |
     | a   | [blank] |
@given("A blank value")
public void givenABlankValue(Map<String, String> map){
    // map contains { "key":"a", "value": ""}
}

@DataTableType(replaceWithEmptyString = "[blank]")
public String listOfStringListsType(String cell) {
    return cell;
}

Note that this only applies to String and Object. It is not possible to redefine other build in types. Though this could be considered if there is a clear use case.

v5.3.0: Sensible CLI defaults

The Cucumber CLI is rather complex to use. To work correctly it needs both a --glue parameter in the form of a package name, and a location of a feature file in form of a classpath uri or path. As a result people often configure Cucumber incorrectly and are left wondering why their features or glue cannot be found.

This can be simplified by using sensible default for both. Unless explicitly told otherwise Cucumber will assume that glue and feature files reside in the class path root. So with the following project layout the CLI can discover and execute all features without needing additional arguments.

├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │       └── com/example/Application.java
│   └── test
│       ├── java
│       │   └── com/example/StepDefinitions.java
│       └── resources
│           └── com/example/example.feature

This can be done with a single maven command:

mvn exec:java                                  \
    -Dexec.classpathScope=test                 \
    -Dexec.mainClass=io.cucumber.core.cli.Main

v5.3.0: Skip Scenarios with the JUnit Platform

The JUnit Platform supports skipping tests. For example in JUnit Jupiter a test can be annotated with @Disabled. This test will be marked as skipped.

Cucumber scenarios do not have annotations nor is there any support to disable specific scenarios. A typical work around from JUnit 4 was to set tags="not @Disabled" in @CucumberOptions and tag scenarios with @Disabled.

To do the same with the Cucumber JUnit Platform Engine the cucumber.filter.tags property can be used. For example by adding cucumber.filter.tags=not @Disabled to junit-platform.properties.

Note: unlike JUnit 4, skipped scenarios are not remove from in the test hierarchy.

v5.5.0: @ParameterType(useRegexpMatchAsStrongTypeHint)

When using regular expressions, Cucumber will use the pattern in a capture group as hint to determine which parameter type should be used.

When the type hint provided by the regex, and the type hint provided by the method (i.e. the types of its arguments) disagree about the type cucumber-expressions prefers the hint provided by the regex.

When declaring parameters types with a very simple regular expressions this may cause a problem. For example:

import io.cucumber.java.ParameterType;
import io.cucumber.java.en.Given;

public class Main {

    private static class TypeA {}

    @ParameterType("\\w+")
    public TypeA typeA(String a) {
        return new TypeA();
    }

    @given("a cucumber expression of {typeA}")
    public void an_object_of_type_a(TypeA a) {
        // works fine!
    }

    @given("^a regular expression of (\\w+)$")
    public void an_object_of_type_b(String b) {
        // broken: will attempt to use the transformer for TypeA
    }
}

By using @ParameterType(pattern="\\w+", useRegexpMatchAsStrongTypeHint=false) this behaviour can be changed to instead prefer the type hint from the method.

Note: useRegexpMatchAsStrongTypeHint will default to false in v6.

v5.5.0: A better way to configure the application context in Cucumber Spring

Cucumber Spring has a complicated way to configure the application context.

The configuration can be provided by either:

  • A context configuration annotation on a class which also happens to have step definitions
  • A magic file named cucumber.xml
  • An empty application context if the previous options could not be discovered

This makes it hard to explain how to use Cucumber Spring and the fallback strategy to the empty application context hides errors.

From now on the preferred way to use cucumber-spring is to annotate a class with both @CucumberContextConfiguration and a Spring context configuration annotation such as @ContextConfiguration, @SpringBootTest, ect.

import com.example.app;

import org.springframework.boot.test.context.SpringBootTest;

import io.cucumber.spring.CucumberContextConfiguration;

@CucumberContextConfiguration
@SpringBootTest(classes = TestConfig.class)
public class CucumberSpringConfiguration {

}

The alternatives, cucumber.xml and annotating step definitions with a @ContextConfiguration have been deprecated and will be removed in v6.

v5.7.0: Exclusive resources with Junit 5

The JUnit Platform supports parallel execution. To avoid flakey tests when multiple scenarios manipulate the same resource tests can be synchronized on that resource.

To synchronize a scenario on a specific resource the scenario must be tagged and this tag mapped to a lock for a specific resource. A resource is identified by a string and can be either locked with a read-write-lock, or a read-lock.

For example:

Feature: Exclusive resources

 @reads-and-writes-system-properties
 Scenario: first example
   Given this reads and writes system properties
   When it is executed 
   Then it will not be executed concurrently with the second example

 @reads-system-properties
 Scenario: second example
   Given this reads system properties
   When it is executed
   Then it will not be executed concurrently with the first example

With this configuration:

cucumber.execution.exclusive-resources.reads-and-writes-system-properties.read-write=SYSTEM_PROPERTIES
cucumber.execution.exclusive-resources.reads-system-properties.read=SYSTEM_PROPERTIES

The first scenario tagged with @reads-and-writes-system-properties will lock the SYSTEM_PROPERTIES with a read-write lock and will not be concurrently executed with the second scenario that uses a read lock.