Feature options

To implement a CornichonFeature it is only required to implement the feature function. However, a number of useful options are available using override.

Before and after hooks

Hooks are available to set up and tear down things as usual but this feature is not integrated into the DSL.

Four functions are available in CornichonFeature with self-explanatory names:

Taking a Unit expression

beforeFeature {
  // do side effect here
}

afterFeature {
  // do side effect here
}

Taking a Step expression similar to the main DSL. You can either pass a single regular Step or a WrapperStep like Attach.

Here is an examples with fictitious steps.

beforeEachScenario {
  Attach {
    Given I setup_server
    Then assert setup_successful
  }
}

afterEachScenario{
  Then I cleanup_resources
}

Base URL

Instead of repeating at each HTTP statement the full URL, it is possible to set a common URL for the entire feature by overriding:

override lazy val baseUrl = s"http://localhost:8080"

and then only provide the missing part in the HTTP step definition

 When I get("/superheroes/Batman")

 When I delete("/superheroes/GreenLantern")

You can still override the base URL of a single step by providing the complete URL starting with the HTTP protocol.

Request timeout

The default value for the HTTP request timeout is 2 seconds. As always it can be overridden per scenario.

import scala.concurrent.duration._

override lazy val requestTimeout = 100.millis

Seed

On a failure the initial seed will be provided in the error reporting enabling you to replay the exact same test even if it contains source of randomness such as:

  • randomized placeholders (random-uuid, random-string, random-boolean etc.)
  • property based testing generators & transitions
  • custom steps using ScenarioContext.randomContext
  • RandomMapper as extractor
override lazy val seed: Option[Long] = Some(1L)

Register custom extractors

In some cases it makes sense to declare extractors to avoid code duplication when dealing with session values.

An extractor is responsible to describe using a JsonPath how to build a value from an existing value in session.

For instance if most of your JSON responses contain a field id and you want to use it as a placeholder without always having to manually extract and save the value into the session you can write :

   override def registerExtractors = Map(
     "response-id" -> JsonMapper(HttpService.LastResponseBodyKey, "id")
   )

It is now possible to use <response-id> or <response-id[integer]> in the steps definitions.

It works for all keys in Session, let’s say we also have objects registered under keys customer & product:

   override def registerExtractors = Map(
     "response-version" -> JsonMapper(HttpService.LastResponseBodyKey, "version"),
     "customer-street" -> JsonMapper("customer", "address.street"),
     "product-first-rating" -> JsonMapper("product", "rating[0].score")
   )

Execution model

By default, the features are executed sequentially and the scenarios within are executed in parallel.

This execution is configurable if you have specific constraints.

To run scenarios sequentially it is necessary to declare in your application.conf file

cornichon {
  executeScenariosInParallel = false
}

The actual number of concurrent scenario is controlled via the configuration field scenarioExecutionParallelismFactor which defaults to 1.

number of concurrent scenarios = `scenarioExecutionParallelismFactor` * number of CPU + 1

This means using more powerful machines will automatically trigger more scenarios.

To run features in parallel it is necessary to manually set a flag in your SBT build file.

parallelExecution in Test := true

or through the command line sbt test parallelExecution in Test := true

Ignoring features or scenarios

Feature or individual scenario can also be marked to be ignored.

import com.github.agourlay.cornichon.CornichonFeature

class CornichonExamplesSpec extends CornichonFeature {

  // Ignore a complete feature
  def feature = Feature("Checking google").ignoredBecause("Your reasons ..."){

    // Ignore a single scenario
    Scenario("Google is up and running").ignoredBecause("Your reasons ..."){

      When I get("http://google.com")

      Then assert status.is(302)
    }
  }
}

Pending scenario

During development, you may want to remember that a scenario needs to be created, but you’re not ready to write it yet.

import com.github.agourlay.cornichon.CornichonFeature

class CornichonPendingExamplesSpec extends CornichonFeature {

  def feature = Feature("Some title"){

    Scenario("this important use case").pending

    Scenario("that important edge case").pending
  }
}