2. ● functional language run on jvm
● you can write & mix code in Java
● project developed in Scala
● api tests, browser-based tests, performance test (Gatling)
● easy to switch from application code to test code
● it’s fun to learn and evaluate new tools
● data model
The Why story
3. Test styles
● xUnit
class SomeSuite extends FunSuite {
test("account creation test") {
}
}
● xUnit -> BDD
class SomeSuite extends FlatSpec {
behavior of "account service”
it should "be possible to create account" in {
}
"fresh account" should "have no roles" in {
}
}
4. Test styles
● Feature Given/When/Then
class SomeSuite extends FeatureSpec {
feature("account service list") {
scenario("empty list") {
Given("not accounts")
...
When("I ask for accounts")
...
Then("an empty list should be returned")
}
}
}
● Ruby’s RSpec
class SomeSuite extends FeatureSpec {
describe("account list page") {
scenario("empty list") {
it("should have size 0") {
}
}
}
}
5. Structural features
● pending tests
○ useful when creating test cases first - then implementation
○ listed in report
○ as a manual test cases list
it should "paginate account list" in pending
● informers, notifiers, alerters
○ visible in report
○ colored during test run (command line or CI)
○ logging alternative
alert("something went wrong")
info("something happened")
given("some initial data")
6. Structural features
● documenters
markup {
"""
This is very complicated test
----
It does something but who would remember what.
"""}
it should "be possible to create account" in {
}
}
7. Structural features
● tagging
it should "have valid schema" taggedAs (Schema, Smoke) in {
...
}
● runners
○ possibility to run existing JUnit & TestNG test (easy to migrate)
○ rerunning failed tests (second run)
○ retry on fail (under define conditions)
○ parallel run
○ IDE, maven, sbt
8. Structural features
● reporters
○ listeners for any kind of test event like: TestFailed, SuiteStaring, etc.
○ html, junit xml output formats - easy to integrate with CI
○ use case: custom ignored tests list
9. Test features
● withClue
withClue("active account") {
account.name should be("bar")
}
withClue("inactive account") {
account.name should be("bar")
}
org.scalatest.exceptions.TestFailedException: inactive account "[foo]" was not equal to "[bar]"
10. Test features
● matchers/assertions
○ easy to read API
○ clear failure messages
account.name should startWith ("bar")
“foo" did not start with substring "bar"
account.name should fullyMatch regex "^bar"
"foo" did not fully match the regular expression ^bar
account.id should (be > 10 and be < 11)
12 was greater than 10, but 12 was not less than 11
account.roles should have size 2
List(1, 2, 3) had size 3 instead of expected size 2
11. Test features
account.roles should contain atLeastOneOf (3, 1)
List(2, 4, 6) did not contain at least one of (3, 1)
account.roles should contain inOrder (2,6,4)
List(2, 4, 6) did not contain all of (2, 6, 4) in order
forAll(account.roles) {
_ should be > 3
}
org.scalatest.exceptions.TestFailedException: forAll failed, because:
at index 0, 2 was not greater than 3
in List(2, 4, 6)
12. Test features
account should have (
'id (12),
'name ("bar")
)
The name property had value "foo", instead of its expected value "bar", on object Account(12,foo,List(2, 4, 6))
inside(account) {
case Account(_, name, _) =>
name should startWith("bar")
}
13. Test features
● eventually
eventually {
val account = AccountService.getAccount(12)
account.activity shouldBe true
}
eventually(timeout(5 seconds), interval(10 millis)) {
val account = AccountService.getAccount(12)
account.activity shouldBe true
}
The code passed to eventually never returned normally. Attempted 474 times over 5.00807272 seconds. Last
failure message: false was not equal to true.
14. Scala features
● property base functional testing
Seq(
("/repository/file01.json", 2, (arg: String) => {arg should startWith("foo")}),
("/repository/file02.json", 3, (arg: String) => {arg should endWith("bar")})
).foreach{case (inputFilePath, expectedValue, validationFunction) => {
it should s"successfully process file $inputFilePath" in {
// do some common operations and assertions
validationFunction.apply(fileBody)
}
}}
15. Test features
● sharing fixtures
trait SmallFixtureRepository extends LazyLogging {
object Star {
trait AnonymousSession extends ProgramSession {
val anonymousSession = AccountsService.Star.createAnonymousSession(programSession)
}
trait CustomerAccount extends ProgramSession {
val customerAccount = AccountsService.Star.createCustomerAccountWithPassword(programSession)
}
trait CustomerSession extends CustomerAccount {
val customerSession = AccountsService.Star.createCustomerSession(programSession, customerAccount)
}
}
}
16. Test features
● sharing fixtures
it should "grant customer access" in new Star.CustomerSession {
println(customerSession)
}
it should "block anonymous session" in new Star.AnonymousSession {
println(anonymousSession)
}
it should "access bot types" in new Star.CustomerSession {
println(customerSession)
new Star.AnonymousSession {
println(anonymousSession)
}
}
17. Selenium DSL
click on name("name")
enter("Cheese!")
textField("q").value should be ("Cheese!")
checkbox("cbx1").select()
add cookie ("name1", "value1")
capture to "MyScreenShot"