Alimur Razi Rana
Posted on March 3, 2024
In software engineering, despite developers' confidence in their code, comprehensive testing is indispensable for determining its efficacy. Developers and QA engineers must navigate through diverse testing phases to ascertain the comprehensive functionality of their applications. Two widely embraced methodologies for this purpose, are End-to-End (E2E) testing and Behavior-Driven Development (BDD) testing.
While Cypress and Cucumber are favored tools for such testing, integrating them poses challenges due to their differing configurations. If developers try to work with typescript, it creates one extra configuration layer. Cypress’s configuration changed a lot from their pre-10 version. So maybe this blog can help others when they need to work later.
Numerous tutorials and blogs cover the importance and procedures of E2E testing or BDD testing. However, This article focuses specifically on integration procedures for these testing methodologies.
Now it's time to make our hands dirty. Cypress and cucumber will be integrated into an angular project which by default maintains typescript. So ensure that you have already installed Node.js, npm, and ng package in your work environment.
- Create a new angular project.
ng new angular-cypress-cucumber
- E2E testing needs your project running, so you need to run this angular project by
npm start
- Install @cypress/schematic package, this will help the angular project to run with Cypress.
ng add @cypress/schematic
During installation, @cypress/schematic automatically includes necessary npm packages. Additionally, it generates a Cypress configuration file, "cypress.config.ts" and adjusts the "angular.json" configuration file accordingly. Furthermore, it establishes a dedicated "cypress" sub-directory, including sample test files to help the developer get started.
We will execute test cases on a user list page. On this basic listing page, we can envision below scenarios:
- Initially, the page will display no users.
- Following the click of the "Get Users" button, an API call will be triggered. If the API returns a valid response, the list will appear.
- In case of an error returned by the API, the page will remain unchanged.
Now it is time for BDD
npm i @badeball/cypress-cucumber-preprocessor @cypress/webpack-preprocessor -d
Replace the whole cypress.config.ts with below configuration content
import { defineConfig } from 'cypress';
const webpack = require("@cypress/webpack-preprocessor");
const preprocessor = require("@badeball/cypress-cucumber-preprocessor");
async function setupNodeEvents(on, config) {
await preprocessor.addCucumberPreprocessorPlugin(on, config);
on(
"file:preprocessor",
webpack({
webpackOptions: {
resolve: {
extensions: [".ts", ".js"],
},
module: {
rules: [
{
test: /\.feature$/,
use: [
{
loader: "@badeball/cypress-cucumber-preprocessor/webpack",
options: config,
},
],
},
],
},
},
})
);
return config;
}
export default defineConfig({
e2e: {
'baseUrl': 'http://localhost:4200', // this will be the default page for e2e testing
specPattern: ["**/e2e/**/*.feature"], // all e2e test file’s name should be in this pattern
setupNodeEvents
},
component: {
devServer: {
framework: 'angular',
bundler: 'webpack',
},
specPattern: '**/*.cy.ts'
}
})
The configuration file facilitates the preprocessing of Cucumber-generated test cases, preparing them for execution by Cypress. Furthermore, it adjusts the spec locations, allowing Cypress to discover .feature files for test execution. These feature files encapsulate BDD test cases written in the Gherkin syntax.
In the package.json file add this new property too
"cypress-cucumber-preprocessor": {
"stepDefinitions": "cypress/e2e/bdd-cucumber/step-defination/*.step.cy.ts"
},
Within the existing cypress/e2e directory, create a new folder named bdd-cucumber and create another folder inside bdd-cucumber named features. Navigate to the cypress\e2e\bdd-cucumber\features directory and create a new file named user-page.feature. As previously noted, Cucumber or BDD operates with the Gherkin language. The features within this folder will adhere to its syntax.
user-page.feature
Feature: User List functionality
Background: Prepare user page
Given I am on the user page
Scenario: Users appear in UI when API call succeeds
When I click the "Get Users" button
Then A api call will be requested with valid response
And user list will be appear on successful api request
Scenario: Users do not appear in UI when API call fails
When I click the "Get Users" button to check failed response
Then A api call will be requested with failed response
And User list will not appear on failed api request
You can also install highlight-cucumber vscode extension to colorize those features.
In the bdd-cucumber folder create another folder named step-defination . Cypress test files will be added to this folder.
user-page.step.cy.ts
import {
Given,
When,
Then,
} from '@badeball/cypress-cucumber-preprocessor';
const apiUrl = 'https://jsonplaceholder.typicode.com/users';
Given('I am on the user page', () => {
cy.visit('/');
});
When('I click the "Get Users" button', () => {
cy.intercept('GET', apiUrl).as('getUserList');
cy.contains('Get User').click();
});
Then('A api call will be requested with valid response', () => {
cy.wait('@getUserList').then((interception) => {
expect(interception.request.url).to.eq(apiUrl);
expect(interception.response?.statusCode).to.eq(200);
});
});
Then('user list will be appear on successful api request', () => {
cy.get('span.user-name').its('length').should('be.greaterThan', 0);
});
When('I click the "Get Users" button to check failed response', () => {
cy.intercept('GET', apiUrl, { forceNetworkError: true }).as('getUserList');
cy.contains('Get User').click();
});
Then('A api call will be requested with failed response', () => {
cy.wait('@getUserList').then((interception) => {
expect(interception.request.url).to.eq(apiUrl);
expect(interception.error).to.exist;
expect(interception.error?.message).to.eq('forceNetworkError called');
});
});
Then('User list will not appear on failed api request', () => {
cy.get('span.user-name').should('not.exist');
});
As we have already written our test cases, the next step is to implement a user list page. For simplicity, the Angular project comprises solely html and typescript code to retrieve user data via API.
app.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
type User = {
name: string;
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'angular-cypress-cucumber';
url = "https://jsonplaceholder.typicode.com/users";
users: User[] = [];
constructor(private httpClient: HttpClient) {}
getUsers() {
this.httpClient.get<User[]>(this.url).subscribe(
{
next: (res) => {
this.users = res;
},
error: (err: any) => {
alert("Something went wrong!");
},
complete: () => { }
}
);
}
}
app.component.html
<div>
<button (click)="getUsers()">Get User</button>
<ul>
<li *ngFor="let user of users">
<span class="user-name">{{user.name}}</span>
</li>
</ul>
</div>
Now navigate to the project's directory, and run the command npm run cypress:open
. Before that, ensure that the angular project is running in http://localhost:4200 which is declared as the base URL in the cypress configuration.
Posted on March 3, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.