Friday, January 15, 2021

Cypress configuration and organization

Cypress is a next generation front end testing tool built for the modern web.

Instead of using an external listener, such as Selenium, which can be 'flaky' at times waiting for responses, Cypress has been built to run in the browser so it can more accurately monitor and react to requests.  More information can be found at Cypress.io 

The following will install Cypress, give some configuration and organization recommendations, and show how to persist sessions.  Writing the tests is up to you! (Write your first test)



Install

In the base of your application, make a tests directory, and a cypress directory, as you may have or end up using other test suites

> mkdir tests/cypress

> cd tests/cypress


Simple npm install

> npm install cypress --save-dev


run Cypress

> npx cypress open


Official docs Install Cypress



Organization

While the install of Cypress creates an example skeleton directory 

test/cypress/cypress

It is recommended to create your own directory 'just in case' a npm update decides to do 'something' with those skeleton directories.


create an app or unique name under 

tests/cypress/[app_abbrev]

You can copy from 

tests/cypress/cypress

or create the directories:

  • integration 

where you write and run tests  

  • fixtures 

static data for testing

  • plugins 

enable you to modify or extend Cypress

  • screenshots 

if taken, storage

  • support

loaded automatically before your test files ie global tests configuration

    • commands

additional grouping of common executed test steps

    • callbacks

additional grouping of callbacks which can modify the behaviors of tests



Configuration

Under 

tests/cypress

create three config files,

cypress.json

cypress.env.json

cypress.env.json.example


cypress.json contains global configuration related to Cypress

Add your [app_abbrev] location to the config:

{

    "fixturesFolder": "[app_abbrev]/fixtures",

    "integrationFolder": "[app_abbrev]/integration",

    "pluginsFile": "[app_abbrev]/plugins",

    "screenshotsFolder": "[app_abbrev]/screenshots",

    "supportFile": "[app_abbrev]/support"

}


cypress.env.json contains environment dependent configuration, such as user names for logins

You should create and maintain

cypress.env.json.example 

with the available config options too

An example config:

{

    "web_base_url": "http://app.dev.localhost:8088/",

    "login_username": "yourtestuser@dev.localhost",

    "login_password": "randchars"

}


Note, while Cypress does have a baseUrl config option that can be added to cypress.json, doing so does not allow the url to change per environment/developer/tester.  If you are using a defined centralized test environment, or defined containers, then this should not be an issue.  But to allow the url the app uses during testing to vary between environment/developer/tester, you can add and use your own base url by adding it to cypress.env.json

So instead of 

Cypress.config().baseUrl

You would use

Cypress.env("web_base_url")



Support

The index.js in support is called on every run of a test.  

This is where you can add global tests configuration and behaviors

Note, for easier maintenance, try to keep functionality to one file.


Update

tests/cypress/[app_abbrev]/support/index.js

to contain

import './commands/login_ui';


import './callbacks/stop_on_failure';

import './callbacks/preserve_session';


Support Commands

An example of a common executed test step may be to log in to your app.


Login UI

support/commands/login_ui.js

Create and add the minimal steps to log into your app, which may look similar to:

// https://on.cypress.io/custom-commands

Cypress.Commands.add("login_ui", (email, password) => {

    // minimal info to login via ui

    cy.visit(Cypress.env("web_base_url"));

    cy.url().should("include", "[app_abbrev]");


    let el = cy.get('#login_form [name="username"]');

    el.type(Cypress.env("login_username"));


    el = cy.get('#login_form [name="password"]');

    el.type(Cypress.env("login_password"));


    el = cy.get('#login_submit');

    el.click();

});


Note, instead of using your apps ui to log in for every test, you should create a token or api access to expedite the test


Now you can call the command using one consistent statement


describe("Select that Awesome Thing Test", () => {

    describe("Can Login", () => {

        it("Can login", () => {

            cy.login_ui(Cypress.env("login_username"), Cypress.env("login_password"));

        });

    });

});


Support Callbacks/Behaviors


Stop on Failure:

support/callbacks/stop_on_failure.js

When on step of a test fails, it will often cause the next steps to fail.  

So fail early so the problem can be found quicker.

Create and add

// after each test

// stop test after first failure

afterEach(function () {

    if (this.currentTest.state === 'failed') {

        Cypress.runner.stop()

    }

});


Preserve Sessions:

support/callbacks/preserve_session.js

All tests are supposed to be isolated, so Cypress will often clean up your cookies.

While it would be nice if the cleanup happens after the first test, or the last test in a suite, the clean up will happen after a few tests, which can log you out of your app and make it seem like your app or the test are broken.

To persists your session, which is often stored in a session cookie, create and add:

// once before all tests

// preserve session cookie so don't get 'randomly' logged out after several specs

before(function () {

    Cypress.Cookies.defaults({

        preserve: ['your_sessionid_name']

    });

});



Package.json

While you can run Cypress via

> npx cypress open


You can also add a more common alias to your package.json

  "scripts": {

    "test": "npx cypress open"

  },

And run Cypress via

> npm run test



.gitignore
Update your .gitignore
/tests/cypress/node_modules
/tests/cypress/cypress
/tests/cypress/cypress.env.json


Hopefully the above information helps you setup and use Cypress in a more enjoyable and useful fashion.



-End of Document-

Thanks for reading