Introduction
Gwen = [G]iven [W]hen Th[en]
Gwen is a Gherkin interpreter with built-in automation capabilities that can readily be utilised for end-to-end testing and robotic processing. Automation is achieved through Gherkin bindings in meta specs, composed with the Gwen DSL and maintained alongside your feature files. An embedded Selenium engine executes scenarios according to the meta you provide to perform operations in browsers for you.
Key features
- Fully Gherkin compliant interpreter
- Composable step definitions
- Quick project setup with NPM
- Parallel and dry run execution
- Interactive REPL console
- Configurable settings and launch profiles
- Multiple web element locator mechanisms (including JS)
- JavaScript function bindings
- Multiple report formats
- Multi platform (Windows, Linux and Docker)
See the Gwen FAQ.
Usage
Declare features
Declare feature specs to describe how scenarios should behave.
Feature specs are used to communicate scenarios in the language of your domain. They should be clear, concise and declarative.
Write feature specs without giving any thought to automation.
For example, the following describes how adding items to a todo list should behave.
File: gwen/features/todo.feature
Feature: Add todo items
Scenario: Create todo list
Given a new todo list
When the following items are added
| Item |
| Get the milk |
| Walk the dog |
Then the list will contain 2 items
Compose meta
Compose meta specs to describe how scenarios will execute.
Meta specs are used to describe execution and are written in Gherkin too. Step definitions and bindings are defined in meta so your features remain clean and unconcerned with automation details.
Write meta specs with execution and automation in mind.
For example, the following meta describes how each step in the scenario above can be executed to automate a test against a running todo application.
File: gwen/features/todo.meta
Feature: Todo Meta
@Context
@StepDef
Scenario: a new todo list
Given my todo list can be located by css ".todo-list"
When I navigate to "${todo.page.url}"
Then the page title should contain "TodoMVC"
And my todo list should be empty
@Action
@DataTable
@ForEach
@StepDef
Scenario: the following items are added
Given the todo field can be located by class "new-todo"
When I enter Item in the todo field
Then my todo list should contain Item
@Assertion
@StepDef
Scenario: the list will contain <expected-count> items
Given the item count can be located by css ".todo-count"
Then the item count should contain "$<expected-count>"
And my todo list should be displayed
Declaring step definitions
Each step definition is declared as a @StepDef
annotated scenario with a name that matches that of the step in the feature it binds to. Each one calls out to one or more DSL Steps (or potentially other StepDefs) in its body to perform the specified operations at run time.
The first StepDef in our meta example binds to the first step in the feature and will only execute when it is interpreted for execution by Gwen.
Given a new todo list
@Context
@StepDef
Scenario: a new todo list
Given my todo list can be located by css ".todo-list"
When I navigate to "${todo.page.url}"
Then the page title should contain "TodoMVC"
And my todo list should be empty
Web element selectors
The first thing the above will do is bind the selector for locating the todo list element on the web page to the name my todo list
. Later steps can then reference that element using this name to interact with it and Gwen will locate it dynamically.
Given my todo list can be located by css ".todo-list"
Calls DSL step:
Navigating to a web page
The next step in this StepDef will open a new browser window and navigate to the URL in the todo.page.url
property.
Accessing externalised properties
We could have just hard coded the https://todomvc.com/examples/react/dist URL in the above step, but externalising it enables us to target different environments through string interpolation without changing our meta. This is useful during development when you want to execute against a locally running instance of the application or during testing when it is running on a different host or port. With this approach you can define a settings file per environment and assign the URL for that environment to the todo.page.url
property in each file. Then when you want to target an environment, you pass the settings file for that environment to Gwen and the URL will be resolved accordingly.
- conf
- json
- properties
File: gwen/conf/env/test.conf
todo {
page {
url = "https://todomvc.com/examples/react/dist"
}
}
File: gwen/conf/env/test.json
{
"todo": {
"page": {
"url": "https://todomvc.com/examples/react/dist"
}
}
}
File: gwen/conf/test.properties
todo.page.url = https://todomvc.com/examples/react/dist
Checking the page title
The next step in the same StepDef checks that the page title in the browser window contains the string literal "TodoMVC" in it. If it doesn't then an assertion error will be raised, otherwise execution will resume to the next step. It is generally a good idea when navigating to a page to verify that you did indeed land on that page before proceeding to interact with it. Checking the title is one way to do it which will suffice in this case.
Then the page title should contain "TodoMVC"
Calls DSL step:
Verifying that an element is empty
The last step in this StepDef verifies that the web element containing the current list of todo items is empty. We expect this because we haven't added any items to it yet. Recall that we bound this element in the first step earlier to the name my todo list
. So here we reference it by that name and if Gwen sees that is is empty then execution will resume, otherwise an assertion error will be raised.
Processing records in data tables
The second StepDef uses the @DataTable
and @ForEach
annotations which work together to process each record in the data table of the second step in the feature.
When the following items are added
| Item |
| Get the milk |
| Walk the dog |
@Action
@DataTable
@ForEach
@StepDef
Scenario: the following items are added
Given the todo field can be located by class "new-todo"
When I enter Item in the todo field
Then my todo list should contain Item
Gwen will call the StepDef for each record in the table and bind each field value to its declared name in the header record. The second and third steps in this StepDef will enter each Item
into the todo field
and check that it was added to my todo list
. This is the same my todo list
that we earlier confirmed was absent when we opened the page in the browser. We reuse it here to check that it now contains and displays each item. If it does not, then an assertion error will be raised.
When I enter Item in the todo field
Then my todo list should contain Item
Calls DSL steps:
Passing parameters to StepDefs
The last StepDef accepts the number of expected items from the last step in the feature into a parameter placeholder named expected-count
and then uses that to assert its value against the item count
on the page. In this example, the value 2 will be passed as the parameter.
Then the list will contain 2 items
@Assertion
@StepDef
Scenario: the list will contain <expected-count> items
Given the item count can be located by css ".todo-count"
Then the item count should contain "$<expected-count>"
And my todo list should be displayed
Calls DSL step:
Verifying that an element is displayed
Lastly, the final step in this StepDef verifies that the web element containing the current list of todo items is displayed (which it should be after items have been added).
Behavioural rules and semantics
The following annotations on StepDefs are used to associate behaviour types and enforce the following rules when the gwen.behavior.rules
setting is set to strict
.
StepDefs annotated with.. | can only bind steps tied to keyword.. | because.. |
---|---|---|
@Context | Given | Givens imply context |
@Action | When | Whens imply actions |
@Assertion | Then | Thens imply assertions |
When binding to a step declared with And
, But
, or *
, then the keyword in the first step in the chain of preceeding steps declared with either Given
, When
or Then
will become the tying keyword. Should the topmost step in a chain be declared with *
, then the tying keyword will be Given
.
For example, the following binding will be permitted since the feature step is declared with the Then
keyword and the StepDef is annotated with @Assertion
.
Then the list will contain 2 items
@Assertion
@StepDef
Scenario: the list will contain <expected-count> items
Given the item count can be located by css ".todo-count"
Then the item count should contain "$<expected-count>"
And my todo list should be displayed
If we were to change the keyword in the feature step to When
however, it would not only read poorly but would no longer imply @Assertion
behaviour and Gwen would report the violation. Similarly, if we were to change the behaviour annotation on the StepDef to @Context
, then that behaviour would not be implied by the Then
keyword declared in the feature step.
Strict rules are enabled by default since v3.0.0. In prior versions, lenient was the default, meaning that these and other rules like Given-When-Then order were not enforced.
Associative meta
Gwen will automatically associate and bind all same named feature and meta files discovered in a directory with each other. In the following case, the meta would be considered associative.
features/todo.feature
features/todo.meta
Associative meta discovery is always enabled as of Gwen 4. In prior versions, it was enabled by default and could be turned off.
Launch Gwen
Launch Gwen to bind your meta and execute your features to automate scenarios.
Invoke Gwen
When launched on a directory, Gwen will recursively discover and bind all meta and feature files in that directory and execute all the features.
Set the default target environment to test
by updating the gwen.target.env
setting in the gwen.conf
file in the project root. This will ensure that all settings in the conf/env/test.conf
file are loaded when Gwen is launched and that all ${prop.name}
placeholder references throughout all meta and feature files are resolved.
File: gwen.conf
gwen {
target {
env = "test"
}
}
- Yarn
- npm
- pnpm
Quick setup (see getting started)
yarn add -D @gweninterpreter/gwen-web
yarn gwen init
Execute the feature passing in the -b
option to exit when done:
yarn gwen -b gwen/features/todo.feature
Or execute all features in the gwen/features directory
yarn gwen -b gwen/features
Quick setup (see getting started)
npm i --save-dev @gweninterpreter/gwen-web
npm run gwen init
Execute the feature passing in the -b
option to exit when done:
npm run gwen -- -b gwen/features/todo.feature
Or execute all features in the gwen/features directory
npm run gwen -- -b gwen/features
Quick setup (see getting started)
pnpm add -D @gweninterpreter/gwen-web
pnpm gwen init
Execute the feature passing in the -b
option to exit when done:
pnpm gwen -b gwen/features/todo.feature
Or execute all features in the gwen/features directory
pnpm gwen -b gwen/features
Your Gwen usage need not be confined to just testing. You can automate manually intensive and repetitive processes too!