April 14, 2020
How to start with cypress – End2End testing
Introduction
Sometimes you need to test your application or website to make sure it works as it should.
“End-2-End testing is a software testing methodology to test an application flow from start to finish. The purpose of End-2-End testing is to simulate a real user scenario and validate the system and its components for integration and data integrity.” (https://www.softwaretestinghelp.com/what-is-end-to-end-testing/, 08.04.2020, 12.14 pm)
Cypress is an excellent tool to simulate a real end-user, who walks through the application in the browser and tries different things like pressing a button. It does not test any internal application features like the programmatic reading of variables, only the UI.
The technology
Cypress is a node-based E2E testing tool without dependence on Selenium, WebDriver or Protractor. It is easy to install and runs directly in the browser (or headless in Electron on the CI Server/Commandline). Cypress has an intuitive API and is also easy to learn and understand.
The tool combines different frameworks and libraries such as Chai, Chai-jQuery or Mocha, and you don’t have to install anything yourself.
The tests are usually written in JavaScript, but also sometimes in TypeScript, which is why they run in any browser without languages or driver dependencies. Additionally you have access to the developer tools, which improves the coding and changes are reflected in real time. You can also take screenshots and videos of the tests.
How to install and run the first test
To install cypress use the following commands in your commandline:
1 2 |
npm install npm install cypress --save-dev |
You can leave the default settings as cypress sets itself up.
After finishing you should have a cypress folder in your project. This folder contains other folders like integration (this is where the tests will be written), examples (here are example tests stored), videos and screenshots.
If you want to start cypress with a browser and see the actions the test does, use the following command:
1 |
npx cypress open |
After you have entered this command in your commandline, a new window opens in which you can select the browser and tests to be started.
If you want to start cypress without a UI and a browser use:
1 |
npx cypress run |
After you have entered this command, the tests in the command line are run.
For the first test you need to make a new .js file in the cypress/integration folder with a describe(){}-block and a it(){}-block with the following syntax (you also can compare the syntax with the syntax in the example test files):
1 2 3 4 5 |
describe('example-test', function () { it('example', function () { }); }); |
The name in the describe-block describes what the test does in general and the name in the it-block describes the more precise function the test performs. Therefore a describe block can contain several it blocks.
In the following example the comments describe what the commands do:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
describe('example-test', function () { it('example', function () { //you can also put an url in here cy.visit("localhost:4200/", { //only required if you have to authenticate yourself on the website you visit auth: { username: 'name', password: 'password' } }); //this command allows the cookies on my specific website by clicking on a button with the <strong>css-class</strong> "cc-allow" cy.get(".cc-allow").click(); //wihtout {force: true} => Error because of Angular Material (mat-*) //in here we want to click on the first mat-select of the homepage where the <strong>css-selector</strong> is "mat-select" cy.get('mat-select').first().click({force: true}); //here we want to click the second option from mat-select with the <strong>css-selector</strong> "mat-option" cy.get("mat-option").eq(2).click(); //in this line we want that cypress makes a break for 0.5 seconds cy.wait(500); //here we want to type some text into an input-field with the css-selector "mat-form-field input" with the [formcontrolname=name] cy.get("mat-form-field input").get(`[formcontrolname="name"]`).type("test test"); //in this line we want to get a element by it's <strong>css-id</strong> using "#" cy.get("mat-form-field input").get("#email").type("test@mail.com"); //in this line we want to check a checkbox with the [formcontrolname=privacy] cy.get(`[formControlName="privacy"] input`).check({force: true}); //here we want to click a button which contains the text "request" cy.get("button").contains("request").click(); }); }); |
So you can see that the first important thing is that you have to distinguish between css-selectors (the best and recommended way to select an element), css-classes, css-id’s or other css-specific attributes.
To get an html element via css attributes remember the following things:
- css-selectors: cy.get(“selector”)
- css-classes: cy.get(“.classname”)
- css-id’s: cy.get(“#id”)
- other css-attribute: cy.get(
[formcontrolname=”name”]
)
To avoid errors for example if you are using angular materials use {force: true} when clicking or checking an angular materials button or checkbox.
Commands in cypress
Cypress comes with many commands that are very versatile to use. One example is the eq() command. This can be used to compare values or as a selector for an index of an array or html-select tag. Another example of a versatile command is the should() command. With this command you can check if a certain element is there, for example cookies, or if a certain text is displayed in the browser.
For shure you can get almost any element by using the command contains(), but it’s not almost a good idea. Sometimes or from time to time the names of the elements or their textual content can change, so your command doesn’t work anymore and you have to fix your bug. For this reason you should prefer the get() function and use the css selectors or css attributes, because these names do not change that often.
If you want to checkout all the other available commands please take a look at https://docs.cypress.io/api/api/table-of-contents.html
Problems that can occur
- As above described it is possible that you can’t click() or check() for example if you are using angular materials (my case).
In this case try to fix this problem with {force: true}. This statement “[…] overrides the actionable checks Cypress applies and will automatically fire the events”. https://docs.cypress.io/api/commands/click.html#Coordinates
12cy.get("button").click({force: true});cy.get("checkbox").click({force: true});
If this isn’t enough and the click() or check() doens’t pass, try to use relative coordinates like click(x-coordinate, y-coordinate), where the coordinates must be given in pixels.
123cy.get("button").click(15, 40);//this is also possiblecy.get("button").click(15, 40, {force: true});
Alternatively, instead of the exact coordinates of the element, you can simply specify the rough position, such as click(‘position’).
12cy.get("button").click('topRight');cy.get("button").click('topRight', {force: true}); - Also as above described it could be that css specific classes, id’s or attributes change. So the most important thing when writing e2e tests is that you choose a unique selector, class, id or attribute that does not change as often or never. Be careful when choosing these things, otherwise you will have to permanently fix errors due to your changes in your code.
- If your gitlab runner runs as a docker container, your browser may crash. The gitlab error message looks like the following screenshot.
The suggested solution of setting--ipc=host
unfortunately does not work in my case. Instead you can add
1- npx cypress run --record false --browser chrome --headless
to “script” in your gitlab-ci.yml. Additionally you should add the following code into your cypress/plugins/index.js:
1234567891011module.exports = (on, config) => {// `on` is used to hook into various events Cypress emits// `config` is the resolved Cypress configon('before:browser:launch', (browser = {}, launchOptions) => {if (browser.family === 'chrome') {launchOptions.args.push('--disable-dev-shm-usage')}return launchOptions})}
This should fix the error message and the browser will no longer crash.