Guide to Integrating Cypress and Cucumber with Angular
Tags: cypress, angular, cucumber, bdd
Published: 2024-03-03 08:57:39

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 ](https://www.cypress.io/)and [Cucumber](https://cucumber.io/) 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

```javascript
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](https://cucumber.io/docs/gherkin/reference/).

In the package.json file add this new property too
```json
"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_
```Gherkin
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_
```javascript
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
```javascript
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
```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.

[Github Link](https://github.com/Alimurrazi/angular-cypress-cucumber)