When we talk about automation testing of web applications, Selenium is the most used solution in the IT industry, with multiple language support and rich features. However, as the automation testing world progresses, the newest tools have emerged, like Cypress.io. Cypress is an automated test framework that is becoming a more and more popular alternative to Selenium.
Cypress vs. Selenium
What is Cypress?
Cypress is an open-source end-to-end testing framework based on JavaScript that supports Web and API application testing. It works for the following browsers: Chrome-family browsers (including Electron and Chromium-based Microsoft Edge), WebKit (Safari’s browser engine), and Firefox. Cypress also provides an integrated development environment (IDE) that you load up in your browser. You can use Cypress for end-to-end, unit and integration testing. Cypress testing framework executes tests on an actual browser instance, avoiding the need to download browser drivers manually, which is usually the case with Selenium test automation.
The idea besides Cypress is to run tests using the same stack used during development. Cypress is not just another testing tool. It combines many cool features like running your tests in the browser or headless, screenshots and video recordings, in-browser debugging, asynchronous testing, etc. Cypress also has a CLI that can be used to test your website on various browsers and devices.
What is Selenium?
Selenium is an open-source automation tool that automates web applications for testing purposes. It allows Quality Assurance Engineers to automate test cases for the desired browser by using the Selenium WebDriver library along with a language-specific framework. It supports tests written in multiple languages like Java, Ruby, JavaScript, PHP, etc.
Key Differences
Although Selenium and Cypress are both designed to automate browsers for testing purposes, they differ considerably in terms of architecture and performance.
- Audience – A key difference is that Cypress as a tool is ideal for introducing developers to test automation rather than just a replacement for Selenium. It is because the idea behind Cypress is to run tests using the same stack that is used during development. On the other hand, Selenium is a more general-purpose tool targeted at a broader audience
- Architecture – When we talk about architecture, unlike other testing tools which work outside of the browser and execute remote commands across the network, Cypress is present inside the browser and is executed in the same run loop as your application. With the help of the Node.js server, it interacts with both the front and back end of the applications. This means that along with E2E UI test, Cypress has the possibility to test APIs. Using Cypress’s request() method, we can validate GET, POST, PUT, and DELETE API endpoints
- Ease of Setup – It’s very easy for Cypress to access objects, local storage, browser developer tools, and DOM, which makes this tool very fast and easy for automation. Selenium has a WebDriver API that interacts between browser and browser drivers; for every Browser, a different driver is needed
- Testing – Cypress is explicitly designed for testing web applications, whereas on the other side, Selenium can be used to test a range of different applications.
Cypress – Pros and Cons
Cypress Framework Architecture
Most testing tools like Selenium use a web driver to communicate with the browser and execute remote commands across the network. On the other hand, Cypress engine directly operates inside the browser. It is the browser that executes your test code.
With the following images, we can see the difference between the test execution architecture of Selenium and Cypress:
As we can see in this case of Selenium, each of the browsers has provided its drivers, which interact with the browser instances for the execution of the commands. Communications between all the components through this route are two-way. Also, we need different Browser Drivers for different types of browsers. Simply stated, Selenium runs outside the browser and executes the commands via the network. But on the other side, all the commands executed within the browser in Cypress, as you can see on the following image:
Cypress consists of a few different building blocks. One of them is its own Node.js process running on the Operating system. You can look at this as being a backend. This backend then launches a browser window, sets up a proxy to this browser window, and sets the domain to localhost.
The opened browser window has two iFrames inside. One is for Cypress tests executed on localhost, and another is for our application.
So on the browser, our tests are running, and then on the browser, our application is running as well.
To make it possible to communicate with the application’s iFrame, it injects a <script> tag that also sets its domain to localhost. Because now, both iFrames are running on ‘localhost’, it is possible to access everything of the application. Cypress can access DOM, window objects, local storage, network layer, browser DevTools, etc.
Communication between the Cypress Node.js backend and the Cypress iFrame that is running the tests is through a WebSocket, so there is no JSON wire protocol or POST/GET calls like it was in Selenium. Also, Cypress directly talks with the operating system to capture screenshots, record videos, basic file systems, and network-related operations.
The role of the Proxy is to proxy all HTTP requests from the web application itself to its backend. So, every HTTP request that goes out of the browser goes through the Proxy out to the WEB and back to the browser. So Cypress operates on a network layer and can control and alter every HTTP request. Through the proxy, it will get the request for which test we want to execute.
Because the proxy is part of the Cypress setup, Cypress can act as the man in the middle and spy on, mock, or modify the requests and responses.
So this is how Cypress works, and we must understand that everything runs inside the browser. Our tests are being executed inside the browser in the same run loop as our application. Cypress can change the browser behavior at the run time by handling DOM and modifying the requests and responses of the network on the fly.
How to install Cypress?
The Cypress dashboard and Cypress test runner are the two main components that Cypress will install. Perform the following steps to install Cypress on the local machine.
Install NodeJS
Since Cypress is built using NodeJS, you first need to install NodeJS version 12+ before proceeding to install Cypress.
Once NodeJS is installed, create a project folder under the appropriate directory and initialize it with the following command:
npm init
Install Cypress
Cypress is a single module that bundles all the required properties you need to run the tests. To add Cypress to node-modules, you need to run the following command:
npm install cypress
Once Cypress is installed, you should run the following command to open the Cypress dashboard and add Cypress packages to the project directory.
npx cypress open
When you run this command for the first time, Cypress will run a configuration manager to help you configure it.
In Cypress, starting from version ten, you have two options: E2E Testing and Component Testing.
E2E Testing
E2E Testing is a technique that tests your app from the web browser through to the back end of your application, as well as testing integration with third-party APIs and services. These tests are great at ensuring your entire app functions cohesively. Cypress executes end-to-end tests the same way users interact with your app by using a real browser, visiting URLs, viewing content, clicking links and buttons, etc. Testing this way helps ensure your tests and the user’s experience are the same. Tests are written in code with an API that simulates a real user’s steps.
E2E tests are great at verifying your app runs as intended, from the front end to the back end. However, E2E tests can be more difficult to set up, run and maintain. There are often infrastructure needs in setting up a backend for testing purposes.
Component Testing
A component test focuses on a specific component’s functionality. Component tests differ from the E2E tests in that instead of visiting a URL to pull up an entire app, a component can be mounted and tested on its own. This allows you to focus on testing only the component’s functionality and not worrying about other nuances with testing a component as part of the larger application.
Usually, developers write component tests when they build components. Component testing does not accurately represent a component’s real-world performance – it doesn’t cover how it interacts with other components or how the application functions overall. So component tests do nothing to ensure that all the layers of your app are working well together.
Therefore, a well-tested app combines E2E and component tests, with each set of tests specializing in what they do best.
Set up Cypress for E2E Testing
For this blog, we are going to set up Cypress for E2E testing. On the next screen, Cypress offers to create configuration default files for us:
When the action that creates configuration files is finished, you can now select which browser (Chrome) you want to run the tests. Also, you have the possibility to add previously generated specs as on the following image displayed:
Now you are able to see the Cypress folder added to your project with pre-added tests for sample testing.
Creating a test in Cypress
Now that Cypress has been added to our project let’s create our first test. Our test will open the Atlantbh web page, go to the QA / Test Automation Blog section, and open the first blog in the list.
To do this, first, we will create an open_qa_blog.cy.js file in /cypress/e2e/ folder.
Previously we added some tests to the /cypress/e2e/ folder. So if we want to ignore them, we can edit the cypress.config.js file in the following way:
const { defineConfig } = require('cypress') module.exports = defineConfig({ e2e: { excludeSpecPattern: [ '**/1-getting-started/*', '**/2-advanced-examples/*' ] } })
There is a code in Cypress with previously described steps:
//open_qa_blog.cy.js /// <reference types="cypress" /> describe('Open QA blog on the Atlantbh page', () => { it('opens Atlantbh page', () => { cy.visit('https://www.atlantbh.com') cy.get('.default-logo').should('exist') }) it('opens QA/Test Automation blogs page', () => { //hover over Blog menu cy.contains('a', 'Blog').focus() //from the menu click on QA / Test Automation option cy.get('.sub-menu').then( dropdown => { cy.wrap(dropdown).contains('a', 'QA / Test Automation').click() }) cy.get('h1').should('contain', 'QA/Test Automation') }) it('opens first blog on QA/Test Automation page', () => { //click on first article cy.get('.posts-container').then( allPosts => { cy.wrap(allPosts).get('article').eq(0).click() }) //check that title and hashtag exists cy.get('#page-header-wrap').then( pageHeader => { cy.wrap(pageHeader).get('h1').should('have.class', 'entry-title') cy.wrap(pageHeader).get('a').should('have.class', 'qatest-automation') }) //check that comment section exist cy.get('.comments-section').should('exist') }) })
All of Cypress’s functionality is available under the global cy object you can see above. There is no need for any wait because tests execute one step at a time, waiting for the previous step to complete. It uses an event-based architecture that hooks into Google Chrome’s lifecycle events, enabling it to wait for things like Ajax requests to complete without a timeout mechanism. This leads to reliable and fasts tests
Code Walkthrough
Let us look into the integral aspects of the code:
- Enable Intellisense in the test code by including the statement ///
- The cy.visit() function is used for navigating to the target URL
- The cy.get() function is used to locate the required WebElements on the page
- The cy.wrap() command is used to yield objects which were placed in it and yield its resolved value meaning that when you want to use cypress commands like should, type, click on an object or Jquery element, you might want to wrap it first before using the cypress commands. Simply put, this command is used to convert non-cypress workable elements or objects into cypress workable things. Let us explain this method through an example:
cy.get('.posts-container').then( allPosts => { cy.wrap(allPosts).get('article').eq(0).click() })
Here we will try to click on the first article from the container representing allPosts. First, we are going to find an element with the class ‘posts-container’. The cy.get() will yield us an element, and if you do then right after this, you can work with the element being yielded by the cy.get() command. So now this element is of type jQuery. As we can see, when we hover over allPosts, it tells us it is of type jQuery HTML element, which means that we can run the jQuery methods on this element directly.
If we want to run any Cypress assertion, we have to have the Cypress object. So if we want to go back from the jQuery object to the Cypress object, we use cy.wrap() command. cy.wrap() method is used to convert jQuery element into Cypress element, and now we can use other Cypress commands like click() on that Cypress object.
5. Assertions are raised using should() function, i.e., .should(‘exist’)
Running a Cypress Test
Now that we have created our first test let’s run it. We will first need to run Cypress by using the following command in the terminal:
npx cypress open
On the opened Cypress window, you should be able to select the browser on which you want to run the test. After that, you should be able to see the test we created earlier. To run it, simply click on it.
This will open a new browser window and run the test automatically. You should see the test running in real-time in Cypress on the left and the browser on the right. As you can see in the next photo, the test passed, and you can see the details of what happened:
Cypress Test Reporting
Because Cypress is built on top of Mocha is great because Mocha is a very mature project with many custom extensions. Test results can be generated within elements called reporters. We can write our custom reporter or use an existing one, for example, mochawesome. This reporter generates a standalone HTML report file where you can see all test results, timing information, and even test bodies are included. For some failed tests, screenshots are also available.
Conclusion
Cypress is a robust test automation framework. Although it is still young, it is being continuously developed and is quickly catching up with the other full-stack automation tools on the market.
Cypress and Selenium are both powerful testing tools with unique features and advantages. Cypress is known for its fast and reliable testing and real-time reloading. On the other hand, Selenium is more versatile and can work with a broader range of web development frameworks and programming languages.
Which of these two frameworks you should use on your project depends on many factors:
- Easy to learn – Cypress is generally considered easier to learn than Selenium. It has a simpler API and a more intuitive testing interface which makes it easier for developers to get started with test automation
- Speed – Cypress is generally faster than Selenium. Because Cypress runs tests in the same context as the application being tested, it is able to take advantage of the browser’s internal event system to speed up test execution
- Browser compatibility – Selenium has better browser compatibility than Cypress. Selenium supports a wider range of browsers and browser versions, while Cypress is limited to the latest versions of Chrome, Firefox and Edge
- Testing modern web applications – Cypress is generally more suitable for testing modern web applications. Because modern web applications use more JavaScript and dynamic content, Cypress’s modern approach to testing makes it easier to write and maintain tests for these applications.
Ultimately, the choice between Cypress and Selenium depends on your testing needs and the specific requirements of your web application.
If you liked this blog, read more about QA/Test Automation.