|
| 1 | +# Scala implementation details |
| 2 | + |
| 3 | +This page covers some details about the Cucumber Scala implementation. |
| 4 | + |
| 5 | +## Running a Cucumber test |
| 6 | + |
| 7 | +### Backend |
| 8 | + |
| 9 | +From Cucumber core perspective, the entrypoint of a Cucumber implementation is what is called "backend". |
| 10 | + |
| 11 | +The `BackendServiceLoader` core service looks for a `BackendProviderService` implementation. |
| 12 | +Ours is defined in the class `ScalaBackendProviderService`. |
| 13 | + |
| 14 | +The implementing class also has to be registered as a "Java Service" in the `META-INF/services/io.cucumber.core.backend.BackendProviderService` file (in the `resources` folder). |
| 15 | + |
| 16 | +### Loading the glue |
| 17 | + |
| 18 | +When a Cucumber test starts, a Cucumber Runner starts and a `ScalaBackend` instance is created. |
| 19 | +The `ScalaBackend` instance will be used for running all the scenarios which are part of the test (defined by the _features path_ and the _glue path_). |
| 20 | + |
| 21 | +The first thing the Runner does is to "load the glue", that is find all the hooks and step definitions and register them. |
| 22 | +This is handled by the `ScalaBackend#loadGlue()` method. |
| 23 | + |
| 24 | +#### Scala implementation |
| 25 | + |
| 26 | +In the Cucumber Scala implementation, loading the glue code means: |
| 27 | +- finding all the **classes** inheriting `io.cucumber.scala.ScalaDsl` in the _glue path_, and for each: |
| 28 | + - add it to the `Container` instance provided by Cucumber Core |
| 29 | +- finding all the **objects** singletons instances inheriting `io.cucumber.scala.ScalaDsl` in the _glue path_ and for each: |
| 30 | + - extract the hooks and step definitions from it |
| 31 | + - add the definitions to the `Glue` instance provided by Cucumber Core, as NOT `ScenarioScoped` |
| 32 | + |
| 33 | +Ideally all the glue code should be instantiated further (see next section), this is why we register classes (actually a list of `Class`) to the Container. |
| 34 | +But this cannot work for objects because they are by definitions singletons and already instantiated way before Cucumber. |
| 35 | +Thus, objects are not registered in the Container and their lifecycle is out of Cucumber scope. |
| 36 | + |
| 37 | +### Running a scenario |
| 38 | + |
| 39 | +For each scenario, the `buildWorld()` method of the backend is called. |
| 40 | +This is where the glue code should be initialized. |
| 41 | + |
| 42 | +#### Scala implementation |
| 43 | + |
| 44 | +For each **class** identified when loading the glue: |
| 45 | +- an instance is created by the `Lookup` provided by Cucumber Core |
| 46 | +- hooks and steps definitions are extracted from it |
| 47 | +- definitions are added to the `Glue` instance provided by Cucumber Core, as `ScenarioScoped` |
| 48 | + |
| 49 | +Being `ScenarioScoped` ensure instances are flushed at the end of the scenario and recreated for the next one. |
| 50 | + |
| 51 | +## Scala DSL |
| 52 | + |
| 53 | +The Scala DSL is made in a way that any class instance or object extending it contains what we call a **registry**: |
| 54 | +a list of the hooks and step definitions it contains. |
| 55 | +This is the purpose of `ScalaDslRegistry`. |
| 56 | + |
| 57 | +The registry is populated when the class instance or the object is created. |
| 58 | +Unlike other implementations there is no need to use annotations or reflection here. |
| 59 | +This is actually **similar to the Java8/Lambda implementation**. |
0 commit comments