Cypress - End to End Testing Framework

Bushra Alam - Mar 7 '20 - - Dev Community

Cypress is the new kid in the front-end testing market which is gaining popularity amongst testers and developers alike. It can test anything that runs in a browser. Cypress is open source and its community is very active. Cypress tests are written in JavaScript but let that not scare you, in case you are unfamiliar with JavaScript, majority of the time you would be working with cypress commands which are pretty intuitive to work with.

But having an understanding of JavaScript would be really beneficial.. and so I have written a blog and created a YouTube series: JS for Testers to help you with that as well.

Cypress could be used for:

  • Unit Testing
  • Integration Testing
  • End-to-End Testing

These are some of the outstanding features that Cypress boasts of:
CypressFeatures

We will be discussing all of them as we explore cypress and reap the benefits.


Table of Contents


Cypress and Selenium

Cypress and Selenium exist to serve a very similar purpose but they are as different as they can be. If you are familiar with Selenium, you will be amazed by how simple setting up and getting things running in Cypress is.

Installation is hassle free, writing test is easy, tests are not flaky and tests run really fast.

All of this has been possible because Cypress isn't built on top of Selenium unlike most end-to-end testing tools. In fact, Cypress' architecture is very different from that of Selenium. Selenium executes remote commands through the network, whereas Cypress runs in the same run-loop as your application.

Back to table of contents


If you prefer to watch and learn, hop on to our Cypress series on Youtube
Subscribe to my Youtube Channel - QA Camp!


Cypress Installation

Cypress is all in one. Perhaps this will help you understand:

Cypress Installation

And cypress installs all of this with a single command:

CypressInstallCommand

Let's setup the IDE, create a project and install cypress.

Install IDE

It's a good idea to have an IDE. Visual Studio Code is the preferred IDE for Cypress projects.
You can download Visual Studio Code from here: https://code.visualstudio.com/download

Install Node.js

We need to install Node.js because we need to use npm and npx which get downloaded along with node.js.
You can download node.js from here: https://nodejs.org/en/download/
Once download is complete, launch and run through the installer.
To verify successful installation check the version:

   node -v
   npm -v

Create Project

  1. Create a project folder.
  2. In the terminal go to the project directory and run the following command:

    npm init
    

    This will create a package.json file inside your project folder.

  3. You can open this project in Visual Studio Code.

Install Cypress

As promised, Cypress will install in a single command:

   npm install cypress --save-dev

This will install Cypress desktop app and Cypress CLI. Cypress desktop app is GUI that can be used to run the test in browser. Cypress CLI helps you run cypress tests headlessly.

Launch Cypress

To launch the Cypress desktop app (GUI), run the following command:

   npx cypress open

Cypress GUI

The .js files that you see under examples folder are sample cypress tests downloaded to give you a jump start in the world of Cypress.

Back to table of contents


Commands

Now, I have been saying Cypress is easy but I don't want you to take my word for it. See for yourself. Read the below code line by line and see if it makes sense to you.

Cypress Basic Test

Cypress is intuitive because its English-like.

describe and it come from Mocha, which is a JavaScript test framework.

In the above code we are performing four operations that are most common and would be used in almost all the tests that you write. These are:

  1. Visit a page
  2. Query for an element
  3. Perform an action on the element
  4. Make an assertion

Now let's dive deep and explore the different commands cypress provides to perform these four tasks.

Back to table of contents


1. Launching / Navigating the application

visit

Purpose: Visit a URL.

Syntax:

  • cy.visit(url)
  • cy.visit(url,options)
  • cy.visit(options)

Examples:

// Visit a local server running on http://localhost:8000/
cy.visit('http://localhost:8000/')    

// Visit an application
cy.visit('https://www.acme.com/')  

// Visit an application and wait for 30 seconds for the application to launch
cy.visit('https://www.acme.com/', { timeout: 30000 })

// Submit a form
cy.visit({            
   url: 'http://localhost:3000/cgi-bin/newsletterSignup',
   method: 'POST',
   body: {
      name: 'George Burdell',
      email: 'burdell@microsoft.com'
   }
})

url

Purpose: Get the current URL of the active page.

Syntax:

  • cy.url()
  • cy.url(options)

Examples:

// Yield the current URL as a string
cy.url()

// verifies the curent url is equal to the given string
cy.url().should('eq', 'http://localhost:3000/cgi-bin/newsletterSignup')    

// verifies the current url includes the given string
cy.url().should('include', '/newsletterSignup')

go

Purpose: Navigate back or forward to the previous or next URL in the browser’s history.

Syntax:

  • cy.go(direction)
  • cy.go(direction, options)

Examples:

// Go back in browser’s history
cy.go('back')
cy.go(-1)

// Go forward in browser’s history
cy.go('forward')
cy.go(1)

reload

Purpose: Reload the page.

Syntax:

  • cy.reload()
  • cy.reload(forceReload)
  • cy.reload(options)
  • cy.reload(forceReload, options)

forceReload: Whether to reload the current page without using the cache. 'true' forces the reload without cache.

Examples:

// Reload the page as if the user clicked ‘Refresh’
cy.visit('http://localhost:3000/admin')
cy.get('#undo-btn').click().should('not.be.visible')
cy.reload()
cy.get('#undo-btn').click().should('not.be.visible')

// Reload the page without using the cache
cy.visit('http://localhost:3000/admin')
cy.reload(true)

Back to table of contents


2. Accessing UI Elements

get

Purpose: Get one or more DOM elements.

Syntax:

  • cy.get(selector)
  • cy.get(alias)
  • cy.get(selector, options)
  • cy.get(alias, options)

Selector: property of an element like id, class etc to filter matching DOM elements.
Alias: giving DOM element a name by which it could be referred later. Defined using the .as() command and referenced with the @ character and the name of the alias.

Examples:

// Find the dropdown-menu with the given class name
cy.get('.dropdown-menu')

// Find element(s) with the given data attribute
cy.get('[data-test-id="test-example"]')

// Create and use an alias
cy.get('button[type=submit]').as('submitBtn')
//...hack hack hack...
cy.get('@submitBtn')     // later retrieve the submitBtn

contains

Purpose: Get the DOM element containing the text.

Syntax:

  • .contains(content)
  • .contains(content, options)
  • .contains(selector, content)
  • .contains(selector, content, options)

Things to note:

  • contains() could start a series of commands or could be chained to an existing series of command
  • content could be: String, Number, RegExp

Examples:

<ul>
    <li>apples</li>
    <li>oranges</li>
    <li>bananas</li>
</ul>
// Find the first element containing some text
cy.contains('apples')       // yields <li>apples</li>

// Find the first element with text matching the regular expression
cy.contains(/^b\w+/)       // yields <li>bananas</li>

// Specify a selector to return a specific element
cy.contains('ul', 'apples')       // yields <ul>...</ul>

// When chained to an existing series of commands
cy.get('#checkout-container').contains('Buy Now')
//This will query inside of the <#checkout-container> element.

Access element by index

You can get the first, last or an element at a specific index in an array of elements using first(), last() and eq() respectively.

Examples:

<ul>
    <li>one</li>
    <li>two</li>
    <li>three</li>
    <li>four</li>
    <li>five</li>
</ul>
// Get the first element
cy.get('li').first()      // yield <li>one</li>

// Get the last element
cy.get('li').last()      // yield <li>five</li>

// Get the second element
cy.get('li').eq(1)      // yield <li>two</li>

// Get the second last element
cy.get('li').eq(-2)      // yields <li>four</li>

Access element by relation

You can access parents, children, siblings of an element.

parent- Get the parent DOM element (single level up) of a set of DOM elements.

parents- Get the parent DOM elements (multiple level up) of a set of DOM elements.

parentsUntil- Get all ancestors of each DOM element in a set of matched DOM elements up to, but not including, the element provided.

children- Get the children of each DOM element within a set of DOM elements.

siblings- Get sibling DOM elements.

prev- Get the immediately preceding sibling of each element in a set of the elements.

prevAll- Get all previous siblings of each DOM element in a set of matched DOM elements.

prevUntil- Get all previous siblings of each DOM element in a set of matched DOM elements up to, but not including, the element provided.

next- Get the immediately following sibling of each DOM element within a set of DOM elements.

nextAll- Get all following siblings of each DOM element in a set of matched DOM elements.

nextUntil- Get all following siblings of each DOM element in a set of matched DOM elements up to, but not including, the element provided.

Examples:

<ul class='main-nav'>
    <li>Overview</li>
    <li>Getting started
        <ul class='sub-nav'>
            <li>Install</li>
            <li class='active'>Build</li>
            <li>Test</li>
        </ul>
    </li>
</ul>
// parent 
cy.get('li.active').parent()           // yields .sub-nav

// parents
cy.get('li.active').parents()           // yields [.sub-nav, li, .main-nav]

// parentsUntil
cy.get('li.active').parentsUntil('.main-nav')           // yields [.sub-nav, li]

// children
cy.get('ul.sub-nav').children()              // yields [<li>Install</li>,
                                             //         <li class='active'>Build</li>,
                                             //         <li>Test</li>]

cy.get('ul.sub-nav').children('.active')      // yields [<li class='active'>Build</li>]

// siblings
cy.get('.active').siblings()              // yields [<li>Install</li>, <li>Test</li>]

cy.get('li').siblings('.active')          // yields [<li class='active'>Build</li>]
<ul>
    <li id="fruits" class="header">Fruits</li>
    <li>apples</li>
    <li>oranges</li>
    <li>bananas</li>
    <li id="veggies" class="header">Vegetables</li>
    <li>cucumbers</li>
    <li>carrots</li>
    <li>corn</li>
    <li id="nuts" class="header">Nuts</li>
    <li>walnuts</li>
    <li>cashews</li>
    <li>almonds</li>
</ul>
// prev
cy.get('#veggies').prev()         // yields <li>bananas</li>
cy.get('li').prev('#veggies')     // yields <li id="veggies" class="header">Vegetables</li>    

// prevAll
cy.get('#veggies').prevAll()    // yields [<li>apples</li>, <li>oranges</li>, <li>bananas</li>]
cy.get('li').prevAll('#veggies')    // yields <li id="veggies" class="header">Vegetables</li>

// prevUntil
cy.get('#nuts').prevUntil('#veggies')      // yields [<li>cucumbers</li>
                                           // yields       <li>carrots</li>, <li>corn</li>]

 // next
cy.get('#veggies').next()         // yields <li>cucumbers</li>
cy.get('li').next('#veggies')     //        <li id="veggies" class="header">Vegetables</li>    

// nextAll
cy.get('#nuts').nextAll()    // yields [<li>walnuts</li>, <li>cashews</li>, <li>almonds</li>]
cy.get('li').nextAll('#nuts')    // yields <li id="nuts" class="header">Nuts</li>

// prevUntil
cy.get('#veggies').prevUntil('#nuts')      // yields [<li>cucumbers</li>,                                           
                                           //         <li>carrots</li>, <li>corn</li>]

Access element by position

within- Scopes all subsequent cy commands to within this element. Useful when working within a particular group of elements such as a <form>.

root- Get the root DOM element.

Examples:

<form>
    <input name="email" type="email">
    <input name="password" type="password">
    <button type="submit">Login</button>
</form>
cy.get('form').within(($form) => {
    // cy.get() will only search for elements within form,
    // not within the entire document
    cy.get('input[name="email"]').type('john.doe@email.com')
    cy.get('input[name="password"]').type('password')
    cy.root().submit()   // submits the form yielded from 'within'
})

Back to table of contents


3. Actions on elements

click

Purpose: Click a DOM element.

Syntax:

  • .click()
  • .click(options)
  • .click(position)
  • .click(position, options)
  • .click(x, y)
  • .click(x, y, options)

Examples:

// Click on button
cy.get('button').click() 

// Click on first el containing 'Welcome'
cy.contains('Welcome').click() 

// Click the top right corner of the button
cy.get('button').click('topRight')

// Specify explicit coordinates relative to the top left corner
cy.get('button').click(15, 40)

// Force a click regardless of its actionable state
// https://docs.cypress.io/guides/core-concepts/interacting-with-elements.html#Forcing
cy.get('button').click({ force: true })

// Click all buttons found on the page
cy.get('button').click({ multiple: true })

dblclick

Purpose: Double-click a DOM element.

Syntax:

  • .dblclick()
  • .dblclick(options)

Examples:

// Double click on button
cy.get('button').dblclick() 

// Double click on first el containing 'Welcome'
cy.contains('Welcome').dblclick()

type

Purpose: Type into a DOM element.

Syntax:

  • .type(text)
  • .type(text, options)

Examples:

// Type 'Hello, World' into the 'input'
cy.get('input').type('Hello, World')

// Type a key combination
cy.get('input').type('{shift}{alt}Q')     
// this is the same as a user holding down SHIFT and ALT, then pressing Q

// Special characters sequences
cy.get('#code-input').type('function (num) {return num * num;}', 
                           { parseSpecialCharSequences: false })   
// will not escape { } characters

// Implicit form submission behaviour
cy.get('#username').type('bob@burgers.com')
cy.get('#password').type('password123{enter}')

clear

Purpose: Clear the value of an input or textarea.
It is an alias for .type({selectall}{backspace})

Syntax:

  • .clear()
  • .clear(options)

Examples:

// Clear text input
cy.get('[type="text"]').clear()

// Clear the input and type a new value
cy.get('textarea').clear().type('Hello, World')

check

Purpose: Check checkbox(es) or radio(s). The element must be an <input> with type checkbox or radio.

Syntax:

  • .check()
  • .check(value)
  • .check(values)
  • .check(options)
  • .check(value, options)
  • .check(values, options)

Examples:

// Check all checkboxes
cy.get('[type="checkbox"]').check()

// Check the first checkbox
cy.get('[type="checkbox"]').first().check()

// Select all radios
cy.get('[type="radio"]').check()

// Select the radio with the value of ‘US’
cy.get('[type="radio"]').check('US')

// Check the checkboxes with the values ‘ga’ and ‘ca’
cy.get('[type="checkbox"]').check(['ga', 'ca'])

uncheck

Purpose: Uncheck checkbox(es) or radio(s). The element must be an <input> with type checkbox or radio.

Syntax:

  • .uncheck()
  • .uncheck(value)
  • .uncheck(values)
  • .uncheck(options)
  • .uncheck(value, options)
  • .uncheck(values, options)

Examples:

// Uncheck all checkboxes
cy.get('[type="checkbox"]').uncheck()

// Uncheck the first checkbox
cy.get('[type="checkbox"]').first().uncheck()

// Uncheck the checkboxes with the values ‘ga’ and ‘ca’
cy.get('[type="checkbox"]').uncheck(['ga', 'ca'])

select

Purpose: Select an <option> within a <select>.

Syntax:

  • .select(value)
  • .select(values)
  • .select(value, options)
  • .select(values, options)

Examples:

<select multiple>
    <option value="456">apples</option>
    <option value="457">oranges</option>
    <option value="458">bananas</option>
</select>
// Select the '456' option
cy.get('select').select('456')

// Select the options with the texts “apples” and “bananas”
cy.get('select').select(['apples', 'bananas'])

Back to table of contents


4. Assertions

Before we dive in and see what different cammads are there for assertions, there is a good news - many commands have a default, built-in assertion, or rather have requirements that may cause it to fail without needing an explicit assertion you’ve added.

Here are some examples:

  • cy.visit() expects the page to send text/html content with a 200 status code.
  • cy.get() expects the element to eventually exist in the DOM.
  • cy.contains() expects the element with content to eventually exist in the DOM.
  • .click() expects the element to eventually be in an actionable state.

There are two ways to write assertions in Cypress:

  1. Implicit Subjects: Using .should() and .and()
  2. Explicit Subjects: Using expect

Points to note:

  • Cypress bundles Chai, Chai-jQuery, and Sinon-Chai to provide built-in assertions. You can see a comprehensive list of them here.
  • Using .should() and .and() is the preferred way of making assertions in Cypress.
  • Assertions are automatically retried until they pass or time out.
  • In most cases, .should() and .and() yields the same subject it was given from the previous command. However, some chainers change the subject. The chainers that come from Chai or Chai-jQuery will always document what they return and that will help you know what assertions change the subject and which keep it the same.

should

Purpose: Create an assertion.

Syntax:

  • .should(chainers)
  • .should(chainers, value)
  • .should(chainers, method, value)
  • .should(callbackFn)

Examples:

cy.get('nav').should('be.visible')

cy.get('nav').should('be.disabled')

cy.get('nav').should('have.class', 'active')

cy.get('nav').should('not.have.id', 'Dashbaord')

cy.get('nav').should('have.attr', 'href', '/users')

cy.get('nav').children().should('have.length', 8)

Callback Function:
Say, we have to confirm the text inside each of the three items that appear. We can have 3 commands for 3 assertions:

cy.get('#app div:nth(0)').should('contain', 'first child')  
cy.get('#app div:nth(1)').should('contain', 'second child')  
cy.get('#app div:nth(2)').should('contain', 'third child')

This could be done in a single assertion:

cy.get('#app div')
   .should(($div) => {
      expect($div.eq(0)).to.contain('first child')
      expect($div.eq(1)).to.contain('second child')
      expect($div.eq(2)).to.contain('third child')
   })

and

Purpose: Create an assertion. An alias of .should()

Syntax:

  • .and(chainers)
  • .and(chainers, value)
  • .and(chainers, method, value)
  • .and(callbackFn)

Examples:

cy.get('nav')
    .should('be.visible')
    .and('be.disabled')
    .and('have.class', 'active')

cy.get('nav')
    .should('not.have.id', 'Dashbaord')
    .and('have.attr', 'href', '/users')

Back to table of contents


Executing Test

To run your tests you have got following options:

  • Test can be executed from GUI and from Command Line
  • Test can be executed in browser and in headless mode Also, the test runs automatically when you make some changes and save it. This comes handy when you are writing the test and want to execute it often to check. This is called 'Real time reloads'.

1. Running test from GUI

Running tests from GUI is easy.
First, let's launch the Cypress GUI using the following command:

npx cypress open

This is how it looks:

Cypress GUI

All the .js files are the test files.

To run any test simply click on it. Cypress Test Runner will open and the test will run in it.

Cypress Test Runner

This test runner is very intuitive and very powerful. The Command Log lists all the commands that ran and when you tower over them, the App preview section would should you the application state when the command was executed. This is the much loved 'Time Travel' feature that cypress provides out of the box.

2. Running test from Command Line

Using command line, test can be execute in browser as well as in headless mode.

2.1. Headless Mode

Using command line, by default, tests are run in headless mode. Cypress wil record videos when running headlessly.

Run a single spec file

npx cypress run --spec "cypress/integration/examples/actions.spec.js"

Run multiple spec files

npx cypress run --spec "cypress/integration/examples/actions.spec.js,
cypress/integration/examples/files.spec.js"

Run all spec files in a folder

npx cypress run --spec "cypress/integration/examples/**/*"

Run all the spec files in the project

npx cypress run

2.2. In Browser

To execute tests in browser using command line, you simply need to add '--headed' (for electron browser) or '--browser browserName' for some other browser. The “browser” argument can be set to “chrome”, “canary”, “chromium”, or “electron” to launch a browser detected on your system. Cypress will attempt to automatically find the installed browser for you.

# for electron browser
npx cypress run --headed

# for specifying your prefered browser: chrome, canary, chromium, or electron
cypress run --browser chrome

Back to table of contents


If you prefer to watch and learn, hop on to our Cypress series on Youtube
Subscribe to my Youtube Channel - QA Camp!


You can find a sample project here: https://github.com/bushralam/Cypress


. . . . . .
Terabox Video Player