How to go about front-end testing with Cypress.io — a success story
One of the first challenges I faced when I started working at ASOS was using the Cypress.io framework to write the very first front-end (FE) automated tests for one of our solutions. Most of the experience I had was with Selenium Webdriver, which was an already established and well-known automation framework within the tech community.
As most QAs know, one of the pains of FE testing is making sure that the tests are stable and robust enough to handle the dynamic nature of web pages. It has been a difficult task even with some of the most powerful frameworks, so why would it be any different with Cypress?
Well… we’re about to find out as I’d like to walk you through some of the Cypress basics.
Thankfully, getting started with Cypress is relatively easy. The online documentation is very detailed, and I’d strongly suggest that anyone new to Cypress spends some time going through it before writing their first test.
Cypress.io project structure
We first had to set up a Cypress project, which typically looks something like this 👇
The project structure in Cypress is quite straightforward. All tests need to reside within the integration folder, otherwise, the Cypress runner will not be able to detect them. You can run tests either from the Cypress runner or using the command line.
You will also find two very important configuration files — package.json and cypress.json. Package.json keeps track of any new plugins added to the project, whilst cypress.json enables you to add configuration values and modify the default Cypress behaviour.
Two other folders that are worth mentioning are the fixtures folder and support folder. The fixtures folder usually contains fixed data (typically in JSON format) loaded from a file and while the support folder contains a default commands.js file that groups together reusable functions or methods. You can have multiple commands.js files in the same project.
Writing your first test
In the test example above 👆, we’re attempting to click on the ‘clothing’ button on the ASOS landing page, if it’s visible.
1. The describe() is behavioural logic derived from the Mocha BDD interface and explains which feature is under test
2. The before() is a hook that contains logic that we’d like to run before the actual test
3. cy.visit() is a cypress command that navigates to a specific web URL
4. it() is a Mocha BDD function that provides details about what the test is doing
5. cy.get(‘data-cy=clothing_button’) locates web elements on the page and is the basic building block of any Cypress test. Further actions can be chained off this command
6. .should() is a BDD assertion that verifies whether the button is visible or not
7. .click() clicks on the web element located by cy.get()
What to keep in mind when writing your tests
We do love to follow best practice, and these are some of the things we suggest to keep an eye out for:
- Tests should be concise and test one thing at a time
- Split any tests that are testing different functionalities into separate classes. This sounds obvious 😏 but not doing so can be quite confusing to other people trying to understand your code!
- Whenever locating web elements, avoid hard-coding any values
- Write tests that run independently from each other
- Move setup code in before() or beforeEach() hooks
- Move teardown code in after() and afterEach() hooks, preferably cleaning up before the start of the tests, not after
Cool things Cypress can do
There are a variety of FE behaviours one can automate with Cypress, such as navigating webpages, drag and drop, stubbing requests and responses, form interactions and login (SSO, JWT, etc.) to name a few. In fact, Cypress provides so-called ‘recipes’ that will help you get started with any of these functionalities.
We’ve made good use of most of the capabilities mentioned above in our tests and the most challenging functionalities to date were login with SSO and drag and drop. We were successful with the login functionality, however, we had some trouble implementing the drag and drop mainly because the third-party library used during development turned out to be incompatible with some of the cypress commands. We tried several approaches suggested online, including the testing approach recommended by the authors of the third party library and a ready-to-install drag and drop plugin. We also reached out to the cypress community but so far, the answer remains a mystery.
Moving forward, we will be experimenting with dynamic test data, since we would like to run our tests across different domains (and languages) of the ASOS website. I’m sure that we’ll discover more interesting things about Cypress, so stay tuned…
One limitation we came across when writing the tests was cross-browser support, as it seems that Cypress only offers support for Chrome for the time being. This would have helped to cover more ground. However, it will only be a matter of time until this capability is rolled out.
What we concluded about Cypress.io
Cypress has been greatly valuable to my team in terms of coverage, speed and robustness.
We initially started writing a couple of tests to add coverage for functionality that we were developing at the time, but after realising how easy it was to write and maintain these tests, we added 185 more tests to cover critical end-to-end (E2E) paths in our main solution.
Recently, we’ve also set up a continuous integration (CI) pipeline in Azure DevOps which runs on a nightly basis. It currently takes about 19 mins to run the full test suite, whereas before it used to take an entire day to test manually. Now, imagine repeating these tests to check language localisation — it usually takes about a week!
We’ve also observed very good pass rates and consistency in the test runs.
What can I say? Cypress has made our jobs easier!
Sherrylene Gauci is a QA engineer at ASOS working in Customer Care Tech. You can catch her daily at the local gym, training to become the next Tia-Clair Toomey. Also a big coffee fan.