Cypress – the reason why we’re not stuck with Selenium

data: 23 sierpnia, 2018
czas czytania: 10 min
autor: Piotr Klejnowski

„The web has evolved. Finally, testing has too.” These words welcome us on the main page of the Cypress project, a new framework used to create the E2E tests in web applications. In my opinion, this statement is extremely accurate, and I would like to explain why I believe so.

For a start, let’s refresh our memories on the E2E test. They are designed to verify the performance of all application layers together. To some extent, it can assure us that adding a new functionality has not resulted in the failure of already implemented ones.

The most popular solution for this kind of tasks is indisputably Selenium – a tool that was created over a decade ago. It is worth pointing out that back then websites looked and operated slightly different. They were mostly stateless and a server returned a specific subpage with each request. Currently, web applications can be rich in all kinds of asynchronous functionalities due to which covering all the complicated cases in our application can be sometimes quite a challenge.

As a software developer, I can’t imagine developing applications without any tests, however, writing them has never been my favorite task. Regardless of whether they are domain, integration or UI tests – I’m not particularly enthusiastic about the last ones, even though I’ve had a chance to write some tests in Selenium – the level of complexity associated with writing the code seemed to discourage me effectively from exploring the subject. It was different in the case of Cypress where entry threshold is low, especially for developers actively benefiting from JavaScript, but also for people not familiar with this language. They shouldn’t experience any major problems because, despite the fact Cypress is still in the beta phase, it has extensive documentation and a relatively large community for such a recent framework.

Let’s begin

Let’s start with testing the login page with Cypress.


Adding Cypress.io to our project is pretty simple and only requires us to add a package using the NPM manager:

npm install cypress --save-dev

And basically, that’s it – we’re ready to write the first test. To do so, we create our test in the integration file.

it("Should successfully loads", function () {
  cy.get(".panel-heading").contains("Log in").should("be.visible")
  cy.get("label").contains("Email").should("be.visible")
  cy.get("label").contains("Password").should("be.visible")
  cy.get("label").contains("RememberLogin").should("be.visible")
  cy.get("[type='checkbox']").should("have.attr", "name", "RememberLogin")  
  cy.get("button").contains("Log in").should("be.visible")
  })

We’ve created the first test which, once a page is loaded, checks whether relevant elements of the login page have been displayed correctly. I don’t think we need to describe individual lines of the code as the test is so intuitive that it is easy to decipher the way it works without even knowing its purpose. To run the test, start Cypress using the console command.

npx cypress open

You can now access the so-called Test Runner panel where you can manage all tests from the integration directory.

 

To run the test, select one from the list. You can also run all tests at once. The testing process starts in a new window the results of which can be seen below.

Me, a human

Undoubtedly, one of the biggest advantages of Cypress is the way it behaves in the application. While testing, it tries to behave like a user (not a machine) as much as possible. For example, if any button is obscured and a user can’t click it, Cypress will inform us about that and the test will fail. Similarly, if we want to click several buttons at the same time, it won’t be possible for Cypress as it is impossible for a human. In such a case, we will receive a detailed information with suggestions on how to solve the problem.

Another example is how Cypress manages asynchronous elements (there is no async hell here) when we want to test whether a toast is displayed once a form is sent, we don’t have to use sleep or loop checking. Cypress waits till the end of the request and then till the element appears.

  it("Should validate when login and password is invalid", function () {
    cy.get("#Email").type("zxcv")
    cy.get("#Password").type("qwerty")
    cy.get("#logInButton").click()
    cy.wait(4000) // this is unnecessary
    cy.get("#info").contains("Invalid login attempt.").should("be.visible")
    })

If in the above example we want to check the validation in the case of providing incorrect login data, Cypress noticing the use of cy.get() will wait for some time for the server to respond and for the loading of an element. Same thing, if we use other methods to the cy.visit() navigation or creating direct requests to the server with cy.request().

By default, waiting time for the HTTP request is 30000 ms, note though that the time can be modified. After a given time, we will be informed about the test failure.

Where Selenium cannot help, Cypress applies

As we already know, Cypress is written with JavaScript, the considerable advantage of which is that the test is run with the application code and therefore it is possible to access the code form the test level. It’s extremely helpful for mocking values. What is more, there are no obstacles for the HTTP request modification due to which it is easy, for example, to check whether our authorization token is properly transmitted, thus making sure everything goes well in case a user modifies the request. Let’s consider the below example.

describe("Tests for AntiForgeryToken", function () {
  // variable from config, that contain Identity Server URL
  const identityUrl = Cypress.config("identityServerUrl")

  // command declaration that we are going to use in tests
  // allows us to create request to server
  Cypress.Commands.add("loginByToken", function (token, login, password) {
    cy.request({
      method: "POST",
      failOnStatusCode: false,
      url: `${identityUrl}/Account/Login`,
      form: true,
      body: {
        email: login,
        password: password,
        __RequestVerificationToken: token,
        RememberLogin: false
      }
    })
  })

  it("Should parse token from response body and return 200", function () {
    cy.request(`${identityUrl}/Account/Login`)
      .its("body")
      .then((body) => {
        const $html = Cypress.$(body)
        // when the page is rendered
        // we are trying to find the Request Token in the body of page
        const token = $html.find("input[name=__RequestVerificationToken]").val()

        // POST request with token and login data
        // then we simply verify whether Indentity Server authorized us
        cy.loginByToken(token, "test@test.com", "Test_1234")
          .then((resp) => {
            expect(resp.status).to.eq(200)
          })
      })
  })

  // Optional we can check the case with wrong token
  it("Should return error 400 if token is wrong", function () {
    cy.loginByToken("wrong_token", "test@test.com", "Test_1234")
      .then((resp) => {
        expect(resp.status).to.eq(400)
      })
  })
})

Testing responsive applications

How many times have we created a subpage which looked good in our browser and then our QA specialists informed us about layout problems in lower resolutions? Cypress has a solution to this problem which won’t be popular among people developing applications supporting various resolutions, including mobile ones.

cy.viewport("iphone-6")

In such a simple way, with the use of predefined values, we can run our tests at a resolution corresponding to the devices branded with a nibbled apple. Naturally, it is also possible to enter the X / Y values manually. No more arduous tasks consisting in going through the same functions in N different configurations because, as I have already pointed out, if the layout problems prevent a user from clicking a given button, the test will end with a negative result.

Works for me…

Debugging may give you sleepless nights especially when something doesn’t work for unknown reasons. In the case something goes wrong with the test, Selenium will often give us enigmatic information which won’t be enough to solve the problem. And what about Cypress? The console has the ability to store the states of a website at a given stage of testing, which is invaluable while debugging step by step.

For example, after performing the test, I moved the cursor over the moment in which Cypress was to enter the password. As I result, I can observe that it manages to locate a given field and a red dot indicates its interaction with a given object. Naturally, it’s not the only way to debug. Since it’s JavaScript, nothing prevents you from using the „debugger” function in your code.

  cy.get("#logInButton")
  .then(($selectedElement) => {
    debugger 
  })

Or a shorter version:

cy.get("#logInButton")
  .debug()

In the end, we can still use a standard and well-known DevTools from the browser to manual code debugging.

And what about Continuous Integration?

It is known that every extended project has a CI the role of which is to make sure before deploying to a production whether no one has committed any changes which had not been tested in advance and which would have a negative impact on the system. But Cypress won’t let you down because due to a simple command, that is „npx cypress run„, we are able to run all test via CI and the results will be presented in the console in a transparent manner.

It is worth mentioning that Cypress can also create an mp4 video from the test execution that shows how the test looked like in practice. In the case of failure, we can export such video with a bit of the CI magic, thus communicating the failure of the build.

The world is not perfect

Cypress is still in the beta phase and there are occasional bugs, but none of them should prevent you from working with this tool. In addition, the Test Runner has been working on the Chrome browser (adding drivers to other browsers is just a matter of time), however, with browser-launcher2 it is also possible to run tests on other popular browsers. Cypress is also missing a function of creating tests by recording user’s moves but in my opinion, it’s not so crucial.

Summary

Without a doubt, Cypress should be considered by Software Developers and QA specialists considering the functionality it brings to the world of web applications. In addition, it is not dependant on any frontend or backend frameworks. It will work perfectly with Angular and with an application developed entirely in .Net Core MVC. It has been well thought out, including the complexity of current systems, which allows us to save a lot of time and trouble. I have also observed that similar tests in Selenium need more time to be performed than these in Cypress.io. In this article, I haven’t covered all the possibilities as the subject is so vast we could conduct workshops on the use of this tool.

I recommend everyone to get familiar with the documentation and to check Cypress’s possibilities as it is free even for commercial purposes. When I talked to Gleb Bahmutov, the creator of the Cypress project, I asked him about potential running costs and that’s what he said: „Yes, the test runner is open source and 100% free. You don’t have to use the dashboard, it will be paid for private projects”. So, it’s clear that we only have to pay for an optional dashboard used to debug tests open on CI and to watch videos from their execution in a separate online panel. Naturally, we can’t expect the whole community to switch to a new solution, however, it is worth to know Cypress at least to have something to choose from and not to get stuck with Selenium.

Newsletter IT leaks

Dzielimy się inspiracjami i nowinkami z branży IT. Szanujemy Twój czas - obiecujemy nie spamować i wysyłać wiadomości raz na dwa miesiące.

Subscribe to our newsletter

Administratorem Twoich danych osobowych jest Future Processing S.A. z siedzibą w Gliwicach. Twoje dane będziemy przetwarzać w celu przesyłania cyklicznego newslettera dot. branży IT. W każdej chwili możesz się wypisać lub edytować swoje dane. Więcej informacji znajdziesz w naszej polityce prywatności.

Subscribe to our newsletter

Administratorem Twoich danych osobowych jest Future Processing S.A. z siedzibą w Gliwicach. Twoje dane będziemy przetwarzać w celu przesyłania cyklicznego newslettera dot. branży IT. W każdej chwili możesz się wypisać lub edytować swoje dane. Więcej informacji znajdziesz w naszej polityce prywatności.