Skip to content

Commit fc18ad2

Browse files
committed
✨ feat: implement scalatest integration
1 parent 3cc5f7e commit fc18ad2

File tree

16 files changed

+1006
-4
lines changed

16 files changed

+1006
-4
lines changed

build.sbt

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ val cucumberVersion = "7.17.0"
4242
val jacksonVersion = "2.17.0"
4343
val mockitoScalaVersion = "1.17.31"
4444
val junitVersion = "4.13.2"
45+
val scalatestVersion = "3.2.18"
4546

4647
// Projects and settings
4748

@@ -64,6 +65,7 @@ lazy val root = (project in file("."))
6465
)
6566
.aggregate(
6667
cucumberScala.projectRefs ++
68+
cucumberScalatest.projectRefs ++
6769
integrationTestsCommon.projectRefs ++
6870
integrationTestsJackson.projectRefs ++
6971
integrationTestsPicoContainer.projectRefs ++
@@ -122,6 +124,17 @@ lazy val cucumberScala = (projectMatrix in file("cucumber-scala"))
122124
)
123125
.jvmPlatform(scalaVersions = Seq(scala3, scala213, scala212))
124126

127+
lazy val cucumberScalatest = (projectMatrix in file("scalatest"))
128+
.settings(commonSettings)
129+
.settings(
130+
name := "cucumber-scalatest",
131+
libraryDependencies ++= Seq(
132+
"io.cucumber" % "cucumber-core" % cucumberVersion,
133+
"org.scalatest" %% "scalatest" % scalatestVersion
134+
)
135+
)
136+
.jvmPlatform(scalaVersions = Seq(scala3, scala213, scala212))
137+
125138
// Integration tests
126139
lazy val integrationTestsCommon =
127140
(projectMatrix in file("integration-tests/common"))
@@ -167,6 +180,19 @@ lazy val integrationTestsPicoContainer =
167180
.dependsOn(cucumberScala % Test)
168181
.jvmPlatform(scalaVersions = Seq(scala3, scala213, scala212))
169182

183+
lazy val integrationTestsScalatest =
184+
(projectMatrix in file("integration-tests/scalatest"))
185+
.settings(commonSettings)
186+
.settings(
187+
name := "integration-tests-scalatest",
188+
libraryDependencies ++= Seq(
189+
"org.scalatest" %% "scalatest" % scalatestVersion % Test
190+
),
191+
publishArtifact := false
192+
)
193+
.dependsOn(cucumberScala % Test, cucumberScalatest % Test)
194+
.jvmPlatform(scalaVersions = Seq(scala3, scala213, scala212))
195+
170196
// Examples project
171197
lazy val examples = (projectMatrix in file("examples"))
172198
.settings(commonSettings)
@@ -200,12 +226,12 @@ releaseProcess := Seq[ReleaseStep](
200226
runTest,
201227
setReleaseVersion,
202228
// the 2 following steps are part of the Cucumber release process
203-
//commitReleaseVersion,
204-
//tagRelease,
229+
// commitReleaseVersion,
230+
// tagRelease,
205231
releaseStepCommandAndRemaining("publishSigned"),
206232
releaseStepCommand("sonatypeBundleRelease"),
207233
setNextVersion
208234
// the 2 following steps are part of the Cucumber release process
209-
//commitNextVersion,
210-
//pushChanges
235+
// commitNextVersion,
236+
// pushChanges
211237
)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
Feature: Cukes
2+
3+
Scenario: in the belly
4+
Given I have 4 "cukes" in my belly
5+
Then I am "happy"
6+
7+
Scenario: Int in the belly
8+
Given I have eaten an int 100
9+
Then I should have one hundred in my belly
10+
11+
Scenario: Long in the belly
12+
Given I have eaten a long 100
13+
Then I should have long one hundred in my belly
14+
15+
Scenario: String in the belly
16+
Given I have eaten "numnumnum"
17+
Then I should have numnumnum in my belly
18+
19+
Scenario: Double in the belly
20+
Given I have eaten 1.5 doubles
21+
Then I should have one and a half doubles in my belly
22+
23+
Scenario: Float in the belly
24+
Given I have eaten 1.5 floats
25+
Then I should have one and a half floats in my belly
26+
27+
Scenario: Short in the belly
28+
Given I have eaten a short 100
29+
Then I should have short one hundred in my belly
30+
31+
Scenario: Byte in the belly
32+
Given I have eaten a byte 2
33+
Then I should have two byte in my belly
34+
35+
Scenario: BigDecimal in the belly
36+
Given I have eaten 1.5 big decimals
37+
Then I should have one and a half big decimals in my belly
38+
39+
Scenario: BigInt in the belly
40+
Given I have eaten 10 big int
41+
Then I should have a ten big int in my belly
42+
43+
Scenario: Char in the belly
44+
Given I have eaten char 'C'
45+
Then I should have character C in my belly
46+
47+
Scenario: Boolean in the belly
48+
Given I have eaten boolean true
49+
Then I should have truth in my belly
50+
51+
Scenario: DataTable in the belly
52+
Given I have the following foods :
53+
| FOOD | CALORIES |
54+
| cheese | 500 |
55+
| burger | 1000 |
56+
| fries | 750 |
57+
Then I am "definitely happy"
58+
And have eaten 2250.0 calories today
59+
60+
Scenario: DataTable with args in the belly
61+
Given I have a table the sum of all rows should be 400 :
62+
| ROW |
63+
| 20 |
64+
| 80 |
65+
| 300 |
66+
67+
Scenario: Argh! a snake - to be custom mapped
68+
Given I see in the distance ... =====>
69+
Then I have a snake of length 6 moving east
70+
And I see in the distance ... <====================
71+
Then I have a snake of length 21 moving west
72+
73+
Scenario: Custom object with string constructor
74+
Given I have a person Bob
75+
Then he should say "Hello, I'm Bob!"
76+
77+
Scenario: Custom objects in the belly
78+
Given I have eaten the following cukes
79+
| Color | Number |
80+
| Green | 1 |
81+
| Red | 3 |
82+
| Blue | 2 |
83+
Then I should have eaten 6 cukes
84+
And they should have been Green, Red, Blue
85+
86+
Scenario: Did you know that we can handle call by name and zero arity
87+
Given I drink gin and vermouth
88+
When I shake my belly
89+
Then I should have lots of martinis
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package cukes
2+
3+
import io.cucumber.scalatest.{CucumberSuite, CucumberSuiteOptions}
4+
5+
class RunCukesTest extends CucumberSuite with CucumberSuiteOptions {
6+
7+
override def featuresPath: Seq[String] = Nil
8+
9+
override def gluePackages: Seq[String] = Nil
10+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import io.cucumber.datatable.DataTable
2+
import io.cucumber.scala.{EN, ScalaDsl}
3+
import model.{Cukes, Person, Snake}
4+
import org.junit.Assert.assertEquals
5+
6+
import java.util.{List => JList, Map => JMap}
7+
import scala.annotation.nowarn
8+
import scala.jdk.CollectionConverters._
9+
10+
/** Test step definitions to exercise Scala cucumber
11+
*/
12+
@nowarn
13+
class CukesStepDefinitions extends ScalaDsl with EN {
14+
15+
var calorieCount = 0.0
16+
var intBelly: Int = 0
17+
var longBelly: Long = 0L
18+
var stringBelly: String = ""
19+
var doubleBelly: Double = 0.0
20+
var floatBelly: Float = 0.0f
21+
var shortBelly: Short = 0.toShort
22+
var byteBelly: Byte = 0.toByte
23+
var bigDecimalBelly: BigDecimal = BigDecimal(0)
24+
var bigIntBelly: BigInt = BigInt(0)
25+
var charBelly: Char = 'A'
26+
var boolBelly: Boolean = false
27+
var snake: Snake = null
28+
var person: Person = null
29+
var cukes: JList[Cukes] = null
30+
var gin: Int = 13
31+
var vermouth: Int = 42
32+
var maritinis: Int = 0
33+
34+
Given("""I have {} {string} in my belly""") { (howMany: Int, what: String) =>
35+
}
36+
37+
Given("""^I have the following foods :$""") { (table: DataTable) =>
38+
val maps: JList[JMap[String, String]] =
39+
table.asMaps(classOf[String], classOf[String])
40+
calorieCount =
41+
maps.asScala.map(_.get("CALORIES")).map(_.toDouble).fold(0.0)(_ + _)
42+
}
43+
And("""have eaten {double} calories today""") { (calories: Double) =>
44+
assertEquals(calories, calorieCount, 0.0)
45+
}
46+
47+
Given("""I have eaten an int {int}""") { (arg0: Int) =>
48+
intBelly = arg0
49+
}
50+
Then("""^I should have one hundred in my belly$""") { () =>
51+
assertEquals(100, intBelly)
52+
}
53+
54+
Given("""I have eaten a long {long}""") { (arg0: Long) =>
55+
longBelly = arg0
56+
}
57+
Then("""^I should have long one hundred in my belly$""") { () =>
58+
assertEquals(100L, longBelly)
59+
}
60+
61+
Given("""^I have eaten "(.*)"$""") { (arg0: String) =>
62+
stringBelly = arg0
63+
}
64+
Then("""^I should have numnumnum in my belly$""") { () =>
65+
assertEquals("numnumnum", stringBelly)
66+
}
67+
68+
Given("""I have eaten {double} doubles""") { (arg0: Double) =>
69+
doubleBelly = arg0
70+
}
71+
Then("""^I should have one and a half doubles in my belly$""") { () =>
72+
assertEquals(1.5, doubleBelly, 0.0)
73+
}
74+
75+
Given("""I have eaten {} floats""") { (arg0: Float) =>
76+
floatBelly = arg0
77+
}
78+
Then("""^I should have one and a half floats in my belly$""") { () =>
79+
assertEquals(1.5f, floatBelly, 0.0)
80+
}
81+
82+
Given("""I have eaten a short {short}""") { (arg0: Short) =>
83+
shortBelly = arg0
84+
}
85+
Then("""^I should have short one hundred in my belly$""") { () =>
86+
assertEquals(100.toShort, shortBelly)
87+
}
88+
89+
Given("""I have eaten a byte {byte}""") { (arg0: Byte) =>
90+
byteBelly = arg0
91+
}
92+
Then("""^I should have two byte in my belly$""") { () =>
93+
assertEquals(2.toByte, byteBelly)
94+
}
95+
96+
Given("""I have eaten {bigdecimal} big decimals""") {
97+
(arg0: java.math.BigDecimal) =>
98+
bigDecimalBelly = arg0
99+
}
100+
Then("""^I should have one and a half big decimals in my belly$""") { () =>
101+
assertEquals(BigDecimal(1.5), bigDecimalBelly)
102+
}
103+
104+
Given("""I have eaten {biginteger} big int""") {
105+
(arg0: java.math.BigInteger) =>
106+
bigIntBelly = arg0.intValue()
107+
}
108+
Then("""^I should have a ten big int in my belly$""") { () =>
109+
assertEquals(BigInt(10), bigIntBelly)
110+
}
111+
112+
Given("""I have eaten char '{char}'""") { (arg0: Char) =>
113+
charBelly = 'C'
114+
}
115+
Then("""^I should have character C in my belly$""") { () =>
116+
assertEquals('C', charBelly)
117+
}
118+
119+
Given("""I have eaten boolean {boolean}""") { (arg0: Boolean) =>
120+
boolBelly = arg0
121+
}
122+
Then("""^I should have truth in my belly$""") { () =>
123+
assertEquals(true, boolBelly)
124+
}
125+
126+
Given("""I have a table the sum of all rows should be {int} :""") {
127+
(value: Int, table: DataTable) =>
128+
assertEquals(
129+
value,
130+
table
131+
.asList(classOf[String])
132+
.asScala
133+
.drop(1)
134+
.map(String.valueOf(_: String).toInt)
135+
.foldLeft(0)(_ + _)
136+
)
137+
}
138+
139+
Given("""I see in the distance ... {snake}""") { (s: Snake) =>
140+
snake = s
141+
}
142+
Then("""^I have a snake of length (\d+) moving (.*)$""") {
143+
(size: Int, dir: String) =>
144+
assertEquals(size, snake.length)
145+
assertEquals(Symbol(dir), snake.direction)
146+
}
147+
148+
Given("""I have a person {person}""") { (p: Person) =>
149+
person = p
150+
}
151+
152+
Then("""^he should say \"(.*)\"""") { (s: String) =>
153+
assertEquals(person.hello, s)
154+
}
155+
156+
Given("^I have eaten the following cukes$") { (cs: JList[Cukes]) =>
157+
cukes = cs
158+
}
159+
160+
Then("""I should have eaten {int} cukes""") { (total: Int) =>
161+
assertEquals(total, cukes.asScala.map(_.number).sum)
162+
}
163+
164+
And("^they should have been (.*)$") { (colors: String) =>
165+
assertEquals(colors, cukes.asScala.map(_.color).mkString(", "))
166+
}
167+
168+
Given("^I drink gin and vermouth$") { () =>
169+
gin = 13
170+
vermouth = 42
171+
}
172+
173+
When("^I shake my belly$") { // note the lack of () =>
174+
maritinis += vermouth * gin
175+
}
176+
177+
Then("^I should have lots of martinis$") { () =>
178+
assertEquals(13 * 42, maritinis)
179+
}
180+
}
181+
182+
@nowarn
183+
class ThenDefs extends ScalaDsl with EN {
184+
Then("""^I am "([^"]*)"$""") { (arg0: String) => }
185+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import io.cucumber.scala.ScalaDsl
2+
import model.{Cukes, Person, Snake}
3+
4+
class TypeRegistryConfiguration extends ScalaDsl {
5+
6+
/** Transforms an ASCII snake into an object, for example:
7+
*
8+
* {{{
9+
* ====> becomes Snake(length = 5, direction = 'east)
10+
* ==> becomes Snake(length = 3, direction = 'east)
11+
* }}}
12+
*/
13+
ParameterType("snake", "[=><]+") { s =>
14+
val size = s.length
15+
val direction = s.toList match {
16+
case '<' :: _ => Symbol("west")
17+
case l if l.last == '>' => Symbol("east")
18+
case _ => Symbol("unknown")
19+
}
20+
Snake(size, direction)
21+
}
22+
23+
ParameterType("person", ".+") { s =>
24+
Person(s)
25+
}
26+
27+
ParameterType("boolean", "true|false") { s =>
28+
s.trim.equals("true")
29+
}
30+
31+
ParameterType("char", ".") { s =>
32+
s.charAt(0)
33+
}
34+
35+
DataTableType { (map: Map[String, String]) =>
36+
Cukes(map("Number").toInt, map("Color"))
37+
}
38+
39+
}

0 commit comments

Comments
 (0)