As a software developer, testing is an essential part of ensuring the quality and reliability of our applications. With the latest versions of JUnit and Cucumber, we now have a powerful combination that takes our testing capabilities to the next level.
In this blog post, we'll explore the benefits of using JUnit 5+ with Cucumber 7+ and how this dynamic duo can supercharge your testing process.
Parallel execution
Cucumber executes tests in a sequential manner by default, utilizing a single thread. However, the option to run tests in parallel is available, but it requires explicit activation.
Due to limitations JUnit 4 could only execute features in parallel. This meant that the Scenarios within a feature file never executed in parallel. This is now possible.
To enable parallel execution, set the cucumber.execution.parallel.enabled
configuration parameter to true
, e.g. in junit-platform.properties
.
To control properties such as the desired parallelism and maximum parallelism, Cucumber supports JUnit 5s ParallelExecutionConfigurationStrategy
. More on this here.
Exclusive Resources
To prevent flaky tests when multiple scenarios manipulate the same resource, tests can be synchronized based on that resource.
To synchronize a scenario on a specific resource, the scenario must be tagged, and this tag should be mapped to a lock for that particular resource. A resource is identified by an arbitrary string and can be locked with either a read-write lock or a read lock.
Feature: Exclusive resources
@reads-and-writes-system-properties
Scenario: Example one
Given AUT component reads and writes system properties
When it is performing operations
Then it will not be executed concurrently with the second example
@reads-system-properties
Scenario: Example two
Given AUT component reads system properties
When it is performing operations
Then it will not be executed concurrently with the first examplegh
with this configuration:
cucumber.execution.exclusive-resources.reads-and-writes-system-properties.read-write=java.lang.System.properties
cucumber.execution.exclusive-resources.reads-system-properties.read=java.lang.System.properties
When running the first scenario tagged with @reads-and-writes-system-properties
, it locks the java.lang.System.properties
resource with a read-write lock. It won't run at the same time as the second scenario that locks the same resource with a read lock.
Running tests in isolation
To guarantee isolated scenario execution, use the global resource org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_KEY
to prevent simultaneous runs.
Feature: Isolated scenarios
@isolated
Scenario: isolated example
Given this scenario runs isolated
When it is executed
Then it will not be executed concurrently with the second or third example
Scenario: second example
When it is executed
Then it will not be executed concurrently with the isolated example
And it will be executed concurrently with the third example
Scenario: third example
When it is executed
Then it will not be executed concurrently with the isolated example
And it will be executed concurrently with the second example
with this configuration:
cucumber.execution.exclusive-resources.isolated.read-write=org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_KEY
It is possible that our current feature files contain scenarios that are not independent (which is not recommended). However, when transitioning scripts from JUnit 4 to JUnit 5, we can configure the following property to maintain parallel execution (of JUnit4):cucumber.execution.execution-mode.feature=same_thread
Supported Discovery Selectors and Filters
JUnit 5 introduced a test discovery mechanism as a dedicated feature within the platform. This allows Integrated Development Environments (IDEs) and build tools to identify tests.
Supported DiscoverySelector
s are:
ClasspathRootSelector
ClasspathResourceSelector
ClassSelector
PackageSelector
FileSelector
DirectorySelector
UriSelector
UniqueIdSelector
The only supported DiscoveryFilter
is the PackageNameFilter
and only when features are selected from the classpath. More on this here.
Selecting specific scenarios, rules, and examples
The FileSelector
and ClasspathResourceSelector
support a FilePosition
.
DiscoverySelectors.selectClasspathResource("rule.feature", FilePosition.from(5))
DiscoverySelectors.selectFile("rule.feature", FilePosition.from(5))
The UriSelector
supports URIs with a line
query parameter:
classpath:/com/example/example.feature?line=20
file:/path/to/com/example/example.feature?line=20
Any TestDescriptor
that matches the line and its descendants will be included in the discovery result. For example, selecting a Rule
will execute all scenarios contained within the Rule.
Tags
Cucumber tags are mapped to JUnit tags. It is important to note that the "@"
symbol is not included as part of the JUnit tag.
So the scenarios below are tagged with Smoke
and Sanity
.
@Smoke
@Ignore
Scenario: Example one tagged
Given I tag a scenario
When I select tests with that tag for execution
Then my tagged scenario is executed
@Sanity
Scenario: Example two tagged
Given I tag a scenario
When I select tests with that tag for execution
Then my tagged scenario is executed
When using Maven, tags can be provided from the CLI using the groups
and excludedGroups
parameters. These take a JUnit5 Tag Expression. The example below will execute Another tagged scenario
.
mvn verify -DexcludedGroups="Ignore" -Dgroups="Smoke | Sanity"
Dear Readers,
I hope you are enjoying the content I provide on my blog. As a passionate writer and dedicated software developer, I strive to create valuable and informative articles that resonate with you. Today, I would like to extend an invitation to support my work and help me continue producing high-quality content.
I have set up a Buy Me a Coffee page, a platform that allows readers like you to show their appreciation by making a small donation. Your contribution, no matter how big or small, goes a long way in supporting my efforts and keeping the blog running. You can also sponsor using the links at the bottom of this page.