Fermenter
Fermenter is a tool for running tests written in the english-like language Gherkin.
It aims to be a function programming alternative to CucumberJS by doing away with things like global state and auto-discovery.
You use the same test runner you are used to: Jest, Mocha, Ava.
You also use the same expression parsers as CucumberJS:
- Gherkin language: docs.cucumber.io/gherkin/reference
- Cucumber Expressions: docs.cucumber.io/cucumber/cucumber-expressions
Examples
Below is a very minimal example:
;; Feature'./features/calculator.feature',;
In the above example state
is strongly typed throughout each step.
Below is a more realistic use of state
with object spreading:
;; Feature'./features/calculator.feature',;
The above test is explicitly mapped to the feature file'./features/calculator.feature'
:
Feature: Calculator Scenario: A simple addition test Given I have numbers 3 and 4 When I add the numbers Then I get 7
To run this test we simply use our test runner (in this case Jest) as normal:
$> yarn jest PASS src/tests/calculator.test.ts Feature: Calculator Scenario: A simple addition test ✓ Given: I have numbers 3 and 4 ✓ When: I add the numbers ✓ Then: I get 7
The above output is generated by the default Jest runner, and thus will include any debug, diff, snapshotting and the error stack traces you expect.
Advanced example
Below is a more advanced example calculator.test.ts:
;;; Feature'./features/calculator.feature',;
The above example maps to: calculator.feature.
Api
The project bundles TypeScript definitions and so the library api is easy to discover.
For more examples, see the tests: src/tests
Scenarios and skipping steps
Feature'./test.feature',
Each of the above Scenario is executed with its own state initial state.
The state provided to the Scenario defaults to {}
, an empty object unless specified otherwise by a Background. More on that later.
You may also choose one scenario to run with only
:
Feature'./test.feature',
Skipping behaviour:
- When a individual step is skipped:
- The skipped function is replaced with
(v) => v
. A state passthrough function. - Steps after it are NOT skipped.
- The skipped function is replaced with
- When an entire Scenario is skipped:
- All steps are skipped
Background
Backgrounds are used to define common state/preparation between scenarios.
Building on top of the previous example, lets add a background to supply some initial state to the Scenario.
;; Feature'./features/calculator.feature',;
Some things to note:
- Backgrounds do not require a
name
Background()
will match all Backgrounds in the feature file.Background('My background')
will match only'My background'
- Backgrounds only support
Given()
andGiven().And().And()
steps - The state returned from the last step of a Background will supply all Scenarios in the Feature
- Scenario can be provided an initial state generic
Tables
To use tables defined in your Gherkin, do this:
; Feature'./features/calculator.feature',;
- See the
ITable
type for details and examples: - There are also some tests here: src/lib/__tests__/GherkinTableReader.spec.ts
Scenario Outlines
Scenario Outlines function just like Scenarios, but are run for each provided example in the .feature
.
Feature'./test.feature',;
See the Scenario section for more info.
Typescript tips
The above is a simple example. Your Background state type will likely be quite large and you shouldnt have to manually define a type! We can use features from TS 3.0 to help here, and some types included in Fermenter.
This time, lets infer the type of the background:
;;; /** Lets also make this function async! */; Feature'./features/calculator.feature',;
Yay, we didn't have to define any plumbing types!
Notes:
- The first Scenario's Given will be run after the Background is complete
- Using
AsyncReturnType
allows one to retrieve the promisified (or not) return value of any function
Global hooks
You may utilize this global hook to instrument or alter your steps and their state:
; globallyBeforeEachStep;
Using other test runners
To set your own test runner, pass its test methods when configuring a feature:
Feature, // or /** Here we wrap `Feature` and give it the global variables mocha provides as test methods */
The framework has been tested in Mocha, Jest and Cypress but is expected to work with any which satisfy the test runner method interfaces.
How it works
Coming from CucumberJS
If you're coming from CucumberJS then some functionality is carried over:
- Same gherkin parser
- Same expression parser
How it runs
- Scenarios and ScenarioOutlines are executed with fresh
state
- There is no
this
state
is reduced with each step function- Strong TypeScript support for step
state
- Steps will inherit the
state
type of the previous step return value - You shouldn't need to manually define types for step functions used inline
- Steps will inherit the
- There is no
- Step names are no longer restricted to be unique for every feature file.
- To reuse a step, simply reuse the function itself
- Tests serve as a composition root. No magic happens inside this library.
- Tests are executed by your test runner, which defaults to Jest
- Steps are executed in synchronous order.
- Background steps are executed before Scenario steps
- Features can be run asynchronously depending on your runner (as they are file-separated)
- Each Scenario will also be run synchronously after another for a given
Feature()
definition- This is the default in Jest
- It is possible to define multiple
Feature()
calls to the same.feature
file within many.test.ts
files, which can allow the same feature to be run in parallel inside Jest for example.
- Each Scenario will also be run synchronously after another for a given