Page History
...
In this tutorial, we will create some UI tests as Cucumber Scenario(s)/Scenario Outline(s) and use Cypress to implement the tests in JavaScript.
using Cypressfor Node.js, in JavaScript, with Cucumber.js.
The test (specification) is initially created in Jira as a Cucumber Test and afterwards, it is exported using the UI or the REST API.
Requirements
- nodejs
- npm packages
- cypress
- cypress-cucumber-preprocessor
- cucumber-json-merge
...
For the purpose of this tutorial, we'll use a simple Javascript class implementing a very basic calculator.
Code Block | ||||
---|---|---|---|---|
| ||||
class Calculator {
constructor(x, y) {
this.x = x;
this.y = y;
}
add() {
this.result = this.x + this.y;
}
getResult() {
return this.result;
}
}
module.exports = Calculator; |
We aim to test the sum operation.
dummy website (source-code here) containing just a few pages to support login/logout kind of features; we aim to test precisely those features.
Before However, before moving into the actual implementation, you we need to decide is which workflow we'll use: do we want to use Xray/Jira as the master for writing the declarative specification (i.e. the Gherkin based Scenarios), or do we want to manage those outside using some editor and store them in Git?This tutorial only showcases using Xray/Jira as the master for editing the Cucumber Scenarios/Scenario Outlines., for example?
Info | ||
---|---|---|
| ||
Please see Testing in BDD with Gherkin based frameworks (e.g. Cucumber) for an overview of the possible workflows. The place that you'll use to edit the Cucumber Scenarios will affect your workflow. There are teams that prefer to edit Cucumber Scenarios in Jira using Xray, while there others that prefer to edit them by writing the .feature files by hand using some IDE. |
Using Jira and Xray as master
...
Otherwise, you can create the Test using the standard standard (issue) Create action from Jira's top menu.
In this case, we'll create a Cucumber Test, of Cucumber Type "Scenario".
We can fill out the Gherkin statements immediately on the Jira issue create dialog or we can create the Test issue first and fill out the details on the next screen, from within the Test issue. In the latter case, we can take advantage of the built-in Gherkin editor which provides auto-complete of Gherkin steps.
After the Test is created it will impact the coverage of related "requirement", if any.
The coverage and the test results can be tracked in the "requirement" side (e.g. user story). In this case, it you may see that coverage changed from being UNCOVERED to NOTRUN (i.e. covered and with at least one test not run).
The related statement's code is managed outside of Jira and stored in Git, for example.
In Cypress, tests related code is stored under cypress/integration
directory, which itself contains several other directories. In this case, we've organized them as follows:
cypress/integration/common
: step implementation files, in JavaScript.cypress/integration/pages
: abstraction of different pages, somehow based on the page-objects modelcypress/integration/login
: Cucumber .feature files, containing the tests as Gherkin Scenario(s)/Scenario Outline(s)
Code Block | |||||||
---|---|---|---|---|---|---|---|
| |||||||
import { Given, When } from 'cypress-cucumber-preprocessor/steps';
import LoginPage from '../../pages/login-page';
import LoginResultsPage from '../../pages/login-results-page';
Given(/^browser is opened to login page$/, () => {
LoginPage.visit();
});
When('user {string} logs in with password {string}', (username, password) => {
LoginPage.enter_username(username);
LoginPage.enter_password(password);
LoginPage.pressLogin();
});
Then(/^welcome page should be open$/, () => {
LoginResultsPage.expect().toBeSuccessful();
});
Then(/^error page should be open$/, () => {
LoginResultsPage.expect().toBeUnsuccessful();
}); |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
import { Given, When } from 'cypress-cucumber-preprocessor/steps'; import LoginPage from '../../pages/login-page'; import LoginResultsPage from const assert = require('assert') const {Before, Given, When, Then} = require('cucumber'); const Calculator = require('../../lib/calculator'); let calculator; Given('the numbers {int} and {int}', function (x, y) { calculator = new Calculator(x, y); }); When('they are added together', function () { calculator.add(); }); Then('should the result be {int}', function (expected) { assert.equal(calculator.getResult(), expected) pages/login-results-page'; Given(/^browser is opened to login page$/, () => { LoginPage.visit(); }); When('user {string} logs in with password {string}', (username, password) => { LoginPage.enter_username(username); LoginPage.enter_password(password); LoginPage.pressLogin(); }); Then(/^welcome page should be open$/, () => { LoginResultsPage.expect().toBeSuccessful(); }); Then(/^error page should be open$/, () => { LoginResultsPage.expect().toBeUnsuccessful(); }); |
Code Block | ||||
---|---|---|---|---|
| ||||
@REQ_CALC-7905
Feature: As a user, I can login the applicaiton
Scenario: Valid Login
Given browser is opened to login page
When user "demo" logs in with password "mode"
Then welcome page should be open
Scenario: Invalid Login
Given browser is opened to login page
When user "dummy" logs in with password "password"
Then error page should be open
Scenario Outline: Login With Invalid Credentials Should Fail
Given browser is opened to login page
When user "<username>" logs in with password "<password>"
Then error page should be open
Examples:
| username | password |
| invalid | mode |
| demo | invalid |
| invalid | invalid | |
Code Block | ||||
---|---|---|---|---|
| ||||
@REQ_CALC-7906
Feature: As a user, I can logout the application
Scenario: Valid Logout
Given user is on the welcome page
When user chooses to logout
Then login page should be open |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
import LoginResultsPage from './login-results-page';
const USERNAME_FIELD = 'input[id=username_field]';
const PASSWORD_FIELD = 'input[id=password_field]';
const LOGIN_BUTTON = 'input[type=submit]';
const LOGIN_TEXT = 'LOGIN';
class LoginPage {
static visit() {
cy.visit('/');
}
static enter_username(username) {
cy.get(USERNAME_FIELD)
.type(username);
}
static enter_password(password) {
cy.get(PASSWORD_FIELD)
.type(password);
}
static pressLogin() {
cy.get(LOGIN_BUTTON).contains(LOGIN_TEXT)
.click();
return new LoginResultsPage();
}
}
export default LoginPage; |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
const RESULT_HEADER = 'h1';
class LoginResultsPage {
static expect() {
return {
toBeSuccessful: () => {
cy.get(RESULT_HEADER).should('have.text', 'Welcome Page')
},
toBeUnsuccessful: () => {
cy.get(RESULT_HEADER).should('have.text', 'Error Page')
},
};
}
}
export default LoginResultsPage; |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
const RESULT_HEADER = 'h1';
class LogoutResultsPage {
static expect() {
return {
toBeSuccessful: () => {
cy.get(RESULT_HEADER).should('have.text', 'Login Page')
},
};
}
}
export default LogoutResultsPage; |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
import LoginPage from './login-page'; const LOGOUT_LINK = 'a'; const LOGOUT_TEXT = 'logout'; class WelcomePage { static visit() { cy.visit('/welcome.html'); } static pressLogout() { cy.get(LOGOUT_LINK).contains(LOGOUT_TEXT) .click(); return new LoginPage(); } } export default WelcomePage;}); |
You can then export the specification of the test to a Cucumber .feature file via the REST API, or the Export to Cucumber UI action from within the Test/Test Execution issue or even based on an existing saved filter. A plugin for your CI tool of choice can be used to ease this task.
...
Results are reflected on the covered item (e.g. Story). On its issue screen, coverage now shows that the item is OK based on the latest testing results, that can also be tracked within the Test Coverage panel bellow.
References
- Cypress
- Cypress documentation
- cypress-cucumber-example
- issue related to adding screenshots to the cucumber JSON report(s)
- https://github.com/cucumber/cucumber-js
- Testing in BDD with Gherkin based frameworks (e.g. Cucumber)
- Automated Tests (Import/Export)
- Exporting Cucumber Tests - REST