From a58fc6eacd38c796872ddc8073c29b5bd0e96ee6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ga=C3=ABl=20Jourdan-Weil?=
Date: Sun, 5 Oct 2025 14:32:19 +0200
Subject: [PATCH 1/3] feat: support Jackson 3.x
---
CHANGELOG.md | 3 ++
build.sbt | 27 +++++++++--
...son3DefaultDataTableEntryTransformer.scala | 48 +++++++++++++++++++
...ksonDefaultDataTableEntryTransformer.scala | 5 +-
docs/build.md | 3 +-
docs/default_jackson_datatable_transformer.md | 32 +++++++++++--
.../test/resources/jackson/Jackson.feature | 0
.../test/resources/junit-platform.properties | 0
.../src/test/scala/jackson/JacksonSteps.scala | 0
.../test/scala/jackson/RunJacksonTest.scala | 0
.../test/resources/jackson3/Jackson3.feature | 15 ++++++
.../test/resources/junit-platform.properties | 3 ++
.../test/scala/jackson3/Jackson3Steps.scala | 40 ++++++++++++++++
.../test/scala/jackson3/RunJackson3Test.scala | 18 +++++++
14 files changed, 183 insertions(+), 11 deletions(-)
create mode 100644 cucumber-scala/src/main/scala/io/cucumber/scala/Jackson3DefaultDataTableEntryTransformer.scala
rename integration-tests/{jackson => jackson2}/src/test/resources/jackson/Jackson.feature (100%)
rename integration-tests/{jackson => jackson2}/src/test/resources/junit-platform.properties (100%)
rename integration-tests/{jackson => jackson2}/src/test/scala/jackson/JacksonSteps.scala (100%)
rename integration-tests/{jackson => jackson2}/src/test/scala/jackson/RunJacksonTest.scala (100%)
create mode 100644 integration-tests/jackson3/src/test/resources/jackson3/Jackson3.feature
create mode 100644 integration-tests/jackson3/src/test/resources/junit-platform.properties
create mode 100644 integration-tests/jackson3/src/test/scala/jackson3/Jackson3Steps.scala
create mode 100644 integration-tests/jackson3/src/test/scala/jackson3/RunJackson3Test.scala
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3e7c23d1..0768188d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,9 @@ See also the [CHANGELOG](https://github.com/cucumber/cucumber-jvm/blob/master/CH
### Added
+- [Scala] Support for Jackson 3.x with `Jackson3DefaultDataTableEntryTransformer` trait
+ - Jackson 2.x is still supported with `JacksonDefaultDataTableEntryTransformer` trait
+
### Changed
### Deprecated
diff --git a/build.sbt b/build.sbt
index 3a3e0e6f..e11393da 100644
--- a/build.sbt
+++ b/build.sbt
@@ -42,6 +42,7 @@ scalaVersion := scala213
val cucumberVersion = "7.30.0"
val jacksonVersion = "2.20.0"
+val jackson3Version = "3.0.0"
val mockitoScalaVersion = "1.17.45"
val junit4Version = "4.13.2"
@@ -83,7 +84,8 @@ lazy val root = (project in file("."))
.aggregate(
cucumberScala.projectRefs ++
integrationTestsCommon.projectRefs ++
- integrationTestsJackson.projectRefs ++
+ integrationTestsJackson2.projectRefs ++
+ integrationTestsJackson3.projectRefs ++
integrationTestsPicoContainer.projectRefs ++
examplesJunit4.projectRefs ++
examplesJunit5.projectRefs: _*
@@ -99,6 +101,7 @@ lazy val cucumberScala = (projectMatrix in file("cucumber-scala"))
"io.cucumber" % "cucumber-core" % cucumberVersion,
// Users have to provide it (for JacksonDefaultDataTableTransformer)
"com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion % Provided,
+ "tools.jackson.module" %% "jackson-module-scala" % jackson3Version % Provided,
"org.junit.jupiter" % "junit-jupiter" % junitBom.key.value % Test,
("org.mockito" %% "mockito-scala" % mockitoScalaVersion % Test)
.cross(CrossVersion.for3Use2_13)
@@ -158,12 +161,12 @@ lazy val integrationTestsCommon =
.dependsOn(cucumberScala % Test)
.jvmPlatform(scalaVersions = Seq(scala3, scala213, scala212))
-lazy val integrationTestsJackson =
- (projectMatrix in file("integration-tests/jackson"))
+lazy val integrationTestsJackson2 =
+ (projectMatrix in file("integration-tests/jackson2"))
.settings(commonSettings)
.settings(junit5SbtSupport)
.settings(
- name := "integration-tests-jackson",
+ name := "integration-tests-jackson2",
libraryDependencies ++= Seq(
"org.junit.platform" % "junit-platform-suite" % junitBom.key.value % Test,
"io.cucumber" % "cucumber-junit-platform-engine" % cucumberVersion % Test,
@@ -174,6 +177,22 @@ lazy val integrationTestsJackson =
.dependsOn(cucumberScala % Test)
.jvmPlatform(scalaVersions = Seq(scala3, scala213, scala212))
+lazy val integrationTestsJackson3 =
+ (projectMatrix in file("integration-tests/jackson3"))
+ .settings(commonSettings)
+ .settings(junit5SbtSupport)
+ .settings(
+ name := "integration-tests-jackson3",
+ libraryDependencies ++= Seq(
+ "org.junit.platform" % "junit-platform-suite" % junitBom.key.value % Test,
+ "io.cucumber" % "cucumber-junit-platform-engine" % cucumberVersion % Test,
+ "tools.jackson.module" %% "jackson-module-scala" % jackson3Version % Test
+ ),
+ publishArtifact := false
+ )
+ .dependsOn(cucumberScala % Test)
+ .jvmPlatform(scalaVersions = Seq(scala3, scala213, scala212))
+
lazy val integrationTestsPicoContainer =
(projectMatrix in file("integration-tests/picocontainer"))
.settings(commonSettings)
diff --git a/cucumber-scala/src/main/scala/io/cucumber/scala/Jackson3DefaultDataTableEntryTransformer.scala b/cucumber-scala/src/main/scala/io/cucumber/scala/Jackson3DefaultDataTableEntryTransformer.scala
new file mode 100644
index 00000000..3b6a4b70
--- /dev/null
+++ b/cucumber-scala/src/main/scala/io/cucumber/scala/Jackson3DefaultDataTableEntryTransformer.scala
@@ -0,0 +1,48 @@
+package io.cucumber.scala
+
+import tools.jackson.databind.ObjectMapper
+import tools.jackson.databind.json.JsonMapper
+import tools.jackson.module.scala.ScalaModule
+
+/** This trait register a `DefaultDataTableEntryTransformer` using Jackson
+ * `ObjectMapper`.
+ *
+ * The `[empty]` string is used as default empty string replacement. You can
+ * override it if you need to.
+ *
+ * Note: Jackson is not included with Cucumber Scala, you have to add the
+ * dependency: `tools.jackson.module:jackson-module-scala` to your project if
+ * you want to use this trait.
+ *
+ * For Jackson 2.x, use `JacksonDefaultDataTableEntryTransformer`
+ * instead.
+ */
+trait Jackson3DefaultDataTableEntryTransformer extends ScalaDsl {
+
+ /** Define the string to be used as replacement for empty. Default is
+ * `[empty]`.
+ */
+ def emptyStringReplacement: String = "[empty]"
+
+ /** Create the Jackson ObjectMapper to be used. Default is a simple JsonMapper
+ * with ScalaModule (including all builtin modules) registered.
+ */
+ def createObjectMapper(): ObjectMapper = {
+ val scalaModule = ScalaModule
+ .builder()
+ .addAllBuiltinModules()
+ .build()
+ JsonMapper.builder().addModule(scalaModule).build()
+ }
+
+ private lazy val objectMapper: ObjectMapper = createObjectMapper()
+
+ DefaultDataTableEntryTransformer(emptyStringReplacement) {
+ (fromValue: Map[String, String], toValueType: java.lang.reflect.Type) =>
+ objectMapper.convertValue[AnyRef](
+ fromValue,
+ objectMapper.constructType(toValueType)
+ )
+ }
+
+}
diff --git a/cucumber-scala/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala b/cucumber-scala/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala
index 22965051..c8410d05 100644
--- a/cucumber-scala/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala
+++ b/cucumber-scala/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala
@@ -10,8 +10,11 @@ import com.fasterxml.jackson.module.scala.DefaultScalaModule
* override it if you need to.
*
* Note: Jackson is not included with Cucumber Scala, you have to add the
- * dependency: `com.fasterxml.jackson.module:jackson-module-scala_2.xx` to your
+ * dependency: `com.fasterxml.jackson.module:jackson-module-scala` to your
* project if you want to use this trait.
+ *
+ * For Jackson 3.x, use `Jackson3DefaultDataTableEntryTransformer`
+ * instead.
*/
trait JacksonDefaultDataTableEntryTransformer extends ScalaDsl {
diff --git a/docs/build.md b/docs/build.md
index 434d8169..abb6ee14 100644
--- a/docs/build.md
+++ b/docs/build.md
@@ -12,7 +12,8 @@ The project contains several subprojects:
- `cucumber-scala`: contains the codebase of the Cucumber Scala implementation
- `integration-tests`: contains integration tests projects
- `common`: general integration tests
- - `jackson`: Jackson integration specific tests
+ - `jackson2`: Jackson 2.x integration specific tests
+ - `jackson3`: Jackson 3.x integration specific tests
- `picocontainer`: Picocontainer integration specific tests
- `examples`: contains a sample project
diff --git a/docs/default_jackson_datatable_transformer.md b/docs/default_jackson_datatable_transformer.md
index 6c5d59dd..f4382c8f 100644
--- a/docs/default_jackson_datatable_transformer.md
+++ b/docs/default_jackson_datatable_transformer.md
@@ -6,32 +6,54 @@ It can be used to automatically convert DataTables to case classes without defin
## Add Jackson dependency
+### Jackson 2.x
+
To use this optional transformer, you need to have Jackson Scala in your dependencies.
```xml
com.fasterxml.jackson.module
jackson-module-scala_2.13
- 2.13.3
+ 2.20.0
test
```
Or:
```sbt
-libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.13.3" % Test
+libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.20.0" % Test
```
-
The current version of Cucumber Scala has been tested against Jackson Module Scala **version 2.20.0**.
+### Jackson 3.x
+
+To use this optional transformer, you need to have Jackson Scala in your dependencies.
+
+```xml
+
+ tools.jackson.module
+ jackson-module-scala_2.13
+ 3.0.0
+ test
+
+```
+
+Or:
+```sbt
+libraryDependencies += "tools.jackson.module" %% "jackson-module-scala" % "3.0.0" % Test
+```
+
+The current version of Cucumber Scala has been tested against Jackson Module Scala **version 3.0.0**.
+
## Add the transformer
-The transformer has to be added to your glue code by extending the `JacksonDefaultDataTableEntryTransformer` trait.
+The transformer has to be added to your glue code by extending the `JacksonDefaultDataTableEntryTransformer` (Jackson 2.x)
+or `Jackson3DefaultDataTableEntryTransformer` (Jackson 3.x) trait.
For instance:
```scala
-class MySteps extends ScalaDsl with EN with JacksonDefaultDataTableEntryTransformer {
+class MySteps extends ScalaDsl with EN with Jackson3DefaultDataTableEntryTransformer {
// Your usual glue code
}
```
diff --git a/integration-tests/jackson/src/test/resources/jackson/Jackson.feature b/integration-tests/jackson2/src/test/resources/jackson/Jackson.feature
similarity index 100%
rename from integration-tests/jackson/src/test/resources/jackson/Jackson.feature
rename to integration-tests/jackson2/src/test/resources/jackson/Jackson.feature
diff --git a/integration-tests/jackson/src/test/resources/junit-platform.properties b/integration-tests/jackson2/src/test/resources/junit-platform.properties
similarity index 100%
rename from integration-tests/jackson/src/test/resources/junit-platform.properties
rename to integration-tests/jackson2/src/test/resources/junit-platform.properties
diff --git a/integration-tests/jackson/src/test/scala/jackson/JacksonSteps.scala b/integration-tests/jackson2/src/test/scala/jackson/JacksonSteps.scala
similarity index 100%
rename from integration-tests/jackson/src/test/scala/jackson/JacksonSteps.scala
rename to integration-tests/jackson2/src/test/scala/jackson/JacksonSteps.scala
diff --git a/integration-tests/jackson/src/test/scala/jackson/RunJacksonTest.scala b/integration-tests/jackson2/src/test/scala/jackson/RunJacksonTest.scala
similarity index 100%
rename from integration-tests/jackson/src/test/scala/jackson/RunJacksonTest.scala
rename to integration-tests/jackson2/src/test/scala/jackson/RunJacksonTest.scala
diff --git a/integration-tests/jackson3/src/test/resources/jackson3/Jackson3.feature b/integration-tests/jackson3/src/test/resources/jackson3/Jackson3.feature
new file mode 100644
index 00000000..09e41023
--- /dev/null
+++ b/integration-tests/jackson3/src/test/resources/jackson3/Jackson3.feature
@@ -0,0 +1,15 @@
+Feature: As Cucumber Scala, I want to provide a basic DataTable transformer using Jackson
+
+ Scenario: Use the default transformer with a basic case class
+ Given I have the following datatable
+ | field1 | field2 | field3 |
+ | 1.2 | true | abc |
+ | 2.3 | false | def |
+ | 3.4 | true | ghj |
+
+ Scenario: Use the default transformer with a basic case class and empty values
+ Given I have the following datatable, with an empty value
+ | field1 | field2 | field3 |
+ | 1.2 | true | abc |
+ | 2.3 | false | [blank] |
+ | 3.4 | true | ghj |
\ No newline at end of file
diff --git a/integration-tests/jackson3/src/test/resources/junit-platform.properties b/integration-tests/jackson3/src/test/resources/junit-platform.properties
new file mode 100644
index 00000000..d756c5ba
--- /dev/null
+++ b/integration-tests/jackson3/src/test/resources/junit-platform.properties
@@ -0,0 +1,3 @@
+# Workaround for https://github.com/sbt/sbt-jupiter-interface/issues/142
+# See also https://github.com/cucumber/cucumber-jvm/pull/3023
+cucumber.junit-platform.discovery.as-root-engine=false
\ No newline at end of file
diff --git a/integration-tests/jackson3/src/test/scala/jackson3/Jackson3Steps.scala b/integration-tests/jackson3/src/test/scala/jackson3/Jackson3Steps.scala
new file mode 100644
index 00000000..78a07862
--- /dev/null
+++ b/integration-tests/jackson3/src/test/scala/jackson3/Jackson3Steps.scala
@@ -0,0 +1,40 @@
+package jackson3
+
+import io.cucumber.scala.{
+ EN,
+ Jackson3DefaultDataTableEntryTransformer,
+ ScalaDsl
+}
+
+import scala.jdk.CollectionConverters._
+
+case class MyCaseClass(field1: Double, field2: Boolean, field3: String)
+
+class Jackson3Steps
+ extends ScalaDsl
+ with EN
+ with Jackson3DefaultDataTableEntryTransformer {
+
+ override def emptyStringReplacement: String = "[blank]"
+
+ Given("I have the following datatable") {
+ (data: java.util.List[MyCaseClass]) =>
+ val expected = Seq(
+ MyCaseClass(1.2, true, "abc"),
+ MyCaseClass(2.3, false, "def"),
+ MyCaseClass(3.4, true, "ghj")
+ )
+ assert(data.asScala == expected)
+ }
+
+ Given("I have the following datatable, with an empty value") {
+ (data: java.util.List[MyCaseClass]) =>
+ val expected = Seq(
+ MyCaseClass(1.2, true, "abc"),
+ MyCaseClass(2.3, false, ""),
+ MyCaseClass(3.4, true, "ghj")
+ )
+ assert(data.asScala == expected)
+ }
+
+}
diff --git a/integration-tests/jackson3/src/test/scala/jackson3/RunJackson3Test.scala b/integration-tests/jackson3/src/test/scala/jackson3/RunJackson3Test.scala
new file mode 100644
index 00000000..5ef0515e
--- /dev/null
+++ b/integration-tests/jackson3/src/test/scala/jackson3/RunJackson3Test.scala
@@ -0,0 +1,18 @@
+package jackson3
+
+import io.cucumber.junit.platform.engine.Constants
+import org.junit.platform.suite.api.{
+ ConfigurationParameter,
+ IncludeEngines,
+ SelectPackages,
+ Suite
+}
+
+@Suite
+@IncludeEngines(Array("cucumber"))
+@SelectPackages(Array("jackson3"))
+@ConfigurationParameter(
+ key = Constants.GLUE_PROPERTY_NAME,
+ value = "jackson3"
+)
+class RunJackson3Test
From 53a570fbebd30d1be58efe3a50061366b491bd5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ga=C3=ABl=20Jourdan-Weil?=
Date: Sun, 5 Oct 2025 14:33:11 +0200
Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9D=20docs:=20add=20missing=20item?=
=?UTF-8?q?=20in=20changelog=20for=208.34.0=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0768188d..1f82acee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@ See also the [CHANGELOG](https://github.com/cucumber/cucumber-jvm/blob/master/CH
### Changed
- [Core] Update `cucumber-core` dependency to [7.30.0](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md)
+- Upgrade Scala versions to 2.13.17
## [8.33.0] (2025-09-22)
From 4d4a72a751857c806bb322390dd6659ee9d1ce12 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ga=C3=ABl=20Jourdan-Weil?=
Date: Sun, 5 Oct 2025 15:32:11 +0200
Subject: [PATCH 3/3] ci: stop testing against Java 8 and 11
This is required to be able to test our support to Jackson 3.
Cucumber Core also stopped testing against these versions.
---
.github/workflows/build.yml | 5 ++---
.github/workflows/version-policy-check.yml | 2 +-
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index cb4eb61e..7bb9bb7d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -18,10 +18,9 @@ jobs:
matrix:
os: [ ubuntu-latest ]
java:
- - 8.0
- - 11
- 17
- 21
+ - 25
runs-on: ${{ matrix.os }}
steps:
@@ -40,7 +39,7 @@ jobs:
uses: actions/setup-java@v5
with:
java-version: ${{ matrix.java }}
- distribution: 'adopt'
+ distribution: 'temurin'
- uses: sbt/setup-sbt@v1
diff --git a/.github/workflows/version-policy-check.yml b/.github/workflows/version-policy-check.yml
index ce51b1de..18fbcc29 100644
--- a/.github/workflows/version-policy-check.yml
+++ b/.github/workflows/version-policy-check.yml
@@ -32,7 +32,7 @@ jobs:
uses: actions/setup-java@v5
with:
java-version: ${{ matrix.java }}
- distribution: 'adopt'
+ distribution: 'temurin'
- uses: sbt/setup-sbt@v1