Why and how we are doing E2E Tests

Leandro Inácio
Homeday
Published in
5 min readJul 30, 2020

tl;dr;

Common Vue-Cypress folder structure with test suites grouped by user flow and page classes containing common elements and actions. The test cases pre-conditions are set up using a “wizard” that can either stub or access endpoints to retrieve data and we also have classes for common reused data values.

What?

At Homeday we value the quality of our releases. We strive to release great features that make an impact, and at the same time, we want to make sure we are not breaking anything else with the new code we are changing and adding.

With that in mind, we know that functional and regression tests are an important part of the process. In this article, I’m going to show you how we set up our E2E Tests on our sliders’ project and why we structured it the way it is.

The sliders project

First, let’s understand the project we want to test. The sliders project is a client side application aiming to interact with the user to help with the process of buying or selling a property. It has several different flows — paths that the user can interact until it reaches the last slide.

It’s built using Vue, where we have Vue views using homeday-blocks’ components and these views layout and behavior change based on configuration files (JavaScript objects), like in the image:

Each view can generate many different pages depending on how their configuration files are set.
Each view can generate many different pages depending on how their configuration files are set.
And each view is also reusing many components from homeday-blocks.

The navigation is also set by the JSON attributes, where one page can link to one or many other pages, depending on the option selected.

As you can see we have a lot of code re-usability here, so when we change something in one of the homeday-blocks components, or one of the views, or one of the configuration files we could be breaking something else that we don’t even know about. E2E tests would improve the confidence and productivity of each developer when doing any work here, they can catch many different issues faster than relying just on manual checks.

Cypress and why

The tool we decided to use was Cypress. Highly adopted, easy integration with Vue, we also had previous experiences with it in different projects with great results.

The installation was pretty simple, we just had to follow the steps described on Vue docs:

vue add e2e-cypress

And just like that we have cypress installed and a structure for the tests added under the /tests folder as well as some examples that we can already execute.

But how should we organize them?

It is important to keep in mind that tests have pre-conditions (a state where the application must be before a test can be executed), steps and expected results (what will be the outcome of the step and the final result). For automated tests, we also need to pay attention to the code re-usability, readability and documentation. And here we wanted to go an extra mile, we wanted the code to document the flow we are testing.

In the test examples we got, we can often see cypress (the global object) accessing elements and executing commands directly from the test case interface. It’s also very common to create cypress commands whenever some command lines are often reused.

Like this:

it(‘.submit() — submit a form’, () => {

// https://on.cypress.io/submit

cy.get(‘.action-form’).find(‘[type=”text”]’).type(‘HALFOFF’)

cy.get(‘.action-form’).submit().next().should(‘contain’, ‘Your form has been submitted!’)

})

First things we can clearly abstract and reuse are the elements. Next things are the actions and the validations. We reuse homeday-blocks components in reused Vue views, so the common Page-Class structure is the first thing that comes to mind. With that we were able to add these pages to the global cypress object, in a global beforeEach() and then we don’t need to worry about importing it anymore. So now we can write tests like this:

it(‘submit a checkout for a plot’, () => {

cy.PropertyType.selectPlot();

cy.Checkout.enterDataAndSubmit({ name: “Leandro”, phone: “123321”});

cy.ThankYou.verifySuccess();

})

You know what is going on, you know where it is going on, you know what should go on after that last step… It’s almost a document describing the test case steps and expected results. We also created common data classes, that we imported and added to cy.Data and can be accessed like this:

cy.Data.checkoutInfos.valid.telephone

cy.Data.checkoutInfos.valid.email

cy.Data.checkoutInfos.valid.telephone

cy.Data.checkoutInfos.invalid.telephone.maxLength

To stub or not to stub?

Yes and No.

First, to have a test created and running as soon as possible, we stubbed some endpoints. We created a “Wizard” class (you can call it Helper if you prefer) with “stubber” methods for specific user-flows. It could be called from the test case like this:

cy.Wizard.stubBookingFlow({ hasAppointments: true });

With that in hands we could quickly automate more complex test cases.

But a mocked E2E is not really an E2E, is it? For scenarios where we could setup the pre-conditions accessing the endpoints to gather some data, that’s what we did.

The decision is up to who is writing the test case here.

Was it worth it?

Absolutely.

After some rounds testing, we found this organization very helpful. Of course we also found space for improvements, but with this approach we could make these improvements without having to rewrite everything from scratch. More than that, we found ourselves learning about different flows of the application written by other developers just by looking at the code. Whenever we had false positives we could quickly identify and fix (for example, elements with new locators), and the real bad guys (bugs that block the flow or hide something in the interface) were quickly identified.

And before I go, I want to say thank you for Gregor Pielken for creating the images showing the slider structure.

Do you like what you see?

Give us a clap, leave a comment, or share anywhere. We appreciate feedback.

Also, check out other articles in our blog. We deal with many interesting things, for example:

And if you like what we do and could consider joining our dev team, check out our openings!

--

--