Through this blog, I will guide you all how we at Swiggy integrated cypress.io evolved testing framework to our CI Pipeline and reduced deployment time.
Why End to End Testing?
Generally, as a developer, we write a small unit of code and writes unit test cases for it for any stack. Unit tests are great to know that your independent modules will be working correctly in silos.
There will be a situation where the developer missed passing correct headers to the network request or missed something important. These scenarios are not covered by unit tests as they expect the silos to pass the required information properly. This type of tests generally performed by QA manually and it is time-consuming as well as exhausting. This is why end to end Testing comes into the picture to test the application in a production-like environment.
Why Cypress?

Cypress is a Javascript End-to-End testing framework that allows you to efficiently and logically write tests for front-end code and UI.
Cypress is a completely new testing framework that has been tailor-made for the highly responsive, asynchronous nature of the web today.
End to End Test cases resides in the same repository where code is present, makes it easy to understand feature without depending on the QA team.
Official Documentation link.
Debuggability
You are able to debug directly using familiar tools such as Chrome Dev Tools. The hundreds of custom error messages and stack traces also make it easy to discover where the test failed.
Automatic Waiting
Cypress automatically waits for commands and assertions before moving on for 4 seconds by default and it is a configurable per-command basis. No more async hell. Cypress will wait for elements to exist, wait for elements to stop animating, or wait for specific network requests to finish.
// Give this element 10 seconds to appear
cy.get(‘.my-slow-selector’, { timeout: 10000 })
Practical Architecture
Most testing tools (like Selenium) operate by running outside of the browser and executing remote commands across the network. Cypress instead executes in the same loop as your application. This allows native access to every single object — window, document, a DOM element, the application instance, etc.
Easy Shortcuts
Cypress prevents you from being forced to always act like a user to generate the state for a given situation. Instead, you can programmatically interact and control applications.
Cypress is Like jQuery
In jQuery we use $(‘.my-selector’) to get the element and $(‘.my-selector’).length to check if the element is present.
In cypress, Each method is equivalent to its jQuery counterpart. Use what you know! and You can chain selectors.
cy.get(‘#main-content’).find(‘.article’).children(‘img[src^=”/static”]’).first()
cy.get() looks for ‘#element’, repeating the query until it finds the element! You can now work with it by using .then
cy.get(‘#element’).then(($myElement) => {doSomething($myElement)})
Assertions
cy.get(‘:checkbox’).should(‘be.disabled’)
cy.get(‘form’).should(‘have.class’, ‘form-horizontal’)
cy.get(‘input’).should(‘not.have.value’, ‘US’)
In each of these examples, it’s important to note that Cypress will automatically wait until these assertions pass.
Commands Are Asynchronous
Cypress commands do not return their subjects, they yield them.
Cypress commands use promises internally so you can return values from one to another.
Cypress commands are asynchronous and get queued for execution at a later time. During execution, subjects are yielded from one command to the next, and a lot of helpful Cypress code runs between each command to ensure everything is in order.
Retry-ability
Every command retries to fetch selector and assertion to pass until the timeout is reached. This prevents scenarios when DOM is not loaded.
cy.get(‘.todo-list li’) will wait until it’s available in the DOM.
only last command is retried
If multiple assertions is used in a sentence, each of the assertions needs to be pass to pass that assertion.
Default Assertions
cy.visit() — expects the page to send text/html content with a 200 status code.
cy.request() — expects the remote server to exist and provide a response.
cy.contains() — expects the element with content to eventually exist in the DOM.
cy.get() — expects the element to eventually exist in the DOM.
cy.find() — also expects the element to eventually exist in the DOM.
cy.type() — expects the element to eventually be in a typeable state.
cy.click() — expects the element to eventually be in an actionable state.
cy.its() — expects to eventually find a property on the current subject.
if you are planning to install for linux environments, you need to add packages as per below
apt-get install xvfb libgtk-3-dev libnotify-dev libgconf-2–4 libnss3 libxss1 libasound2
We have installed following packages for cypress:
cypress-plugin-retries: Sometimes due to higher response time, it might also be possible that the selector doesn’t appear and the test case will fail. Upon Retrying it passes generally.
This plugin becomes handy to solve this issue.
Install and add it in cypress plugins file(cypress/plugins/index.js).
npm install cypress-plugin-retries
require(“cypress-plugin-retries/lib/plugin”)(on);
If the application gives console errors cypress generally fails the tests and it becomes flaky.
Useful Cypress tips:
- You can fix it by returning false as below in cypress/support/index.js file
Cypress.on(“uncaught:exception”, err => { return false;});
2. Cypress generally clears all the cookies after every test run. So it might be possible if you are storing csrf token in cookie you need to persist otherwise your backend will give an error. You can do that using below syntax.
Cypress.Cookies.defaults({ whitelist: [“__SW”]});
3. Want to use features of webpack like alias paths and others, you can do it with @cypress/webpack-preprocessor and add it in cypress plugin file.
npm install @cypress/webpack-preprocessor
on(“file:preprocessor”, webpack(options));
4. How would you know that whatever e2e test cases you have written are properly is enough? You can do that by creating a coverage report for cypress. You need to instrument code using Istanbul. It generates a nice coverage report. more info at the link.
npm install @cypress/code-coverage
5. You can use it.only() to run only that particular test case. You can use “ignoreTestFiles” in cypress.json to ignore files which might be running properly.
Commands
cypress open — opens cypress in watch mode, you can select which specs to run through cypress window.
cypress run — runs every specs in terminal one by one and exits if any of tests fails.
Writing Tests
- The tests should be isolated. Every test should not be dependent on the previous test’s state.
- Mutable variables should not be shared between tests.
- Use data-cy attributes for selector.
- Don’t mock too much. Tests should have happy flow scenarios where you call the APIs directly.
We have also integrated cypress with our ci pipeline which runs tests. We use sonarQube to automatically generate code-smells and coverage report which actually covers dead code also.
Other Side of Coin
- Features. No file upload support. No cross-browser testing. Who knows when these things will be covered, as for big projects these features are crucial.
- Page Object Model. It is something that has already been proven by time. Cypress supports a different approach which could be controversial. More detail on this is here: Cypress POM
- It’s only available for only one client (language) i.e for JavaScript only. So to work with it you must know JavaScript: however this might be an advantage for JavaScript application, but I would like to put it as a disadvantage for those who have difficulties with javascript.
- No support for Parallel testing through several browsers. Only support of Parallel CI Machines.
Conclusion
Over time Cypress will end up saving us a lot of developer time (and therefore money). The tests will be in place forever, and so regressions can be spotted much sooner (ideally in local development) and therefore fixed much faster.
End to end testing has been a fun challenge and positive addition to our workflow. It added a boost of confidence in our codebase and our Infrastructure.
Material:
- https://kentcdodds.com/blog/write-fewer-longer-tests
- https://kentcdodds.com/blog/write-tests
- https://itnext.io/cypress-io-best-practices-for-maintainable-tests-e9b9f392f117
- https://docs.cypress.io/guides/references/best-practices.html
- https://www.cypress.io/blog/2018/01/16/end-to-end-snapshot-testing/
- https://glebbahmutov.com/blog/ssr-e2e/
