Easy E2E Testing with Cypress.io

Javier Villanueva

Reacticon 2018

About me...

Javier Villanueva

@jahvi

http://jahvi.com

javier@medialounge.co.uk

kateandtoms.com

What is testing?

  • Increase code quality
  • Find bugs early
  • Makes changes easier
  • Reduces costs
  • Many more...

Testing is hard...

  • More time writing tests than actual functional code
  • Learning new programming languages
  • Learning new tooling
  • Learning about the right type of test (unit, functional, integration, etc...)

Most tests don’t give us enough confidence

E2E

UNIT

INTEGRATION

Increasing scope

More confidence

Faster

Better isolation

What is Cypress.io?

Fast, easy and reliable testing for anything that runs in a browser

Time Travel

Debuggability

Real time reloads

Automatic waiting

Best Features

beforeEach(() => {
    cy.viewport(1440, 900);
    cy.visit('https://demo.vuestorefront.io');
    cy.get('[data-testid="accountButton"]').click();
});

describe('Login', () => {
    it('displays error message on invalid credentials', () => {
        cy.get('input[name="email"]').type('test@email.com');
        cy.get('input[name="password"]').type('test{enter}');
        cy.get('[data-testid="notificationMessage"]').should(
            'contain',
            'You did not sign in correctly or your account is temporarily disabled'
        );
    });

    it('displays success message on valid credentials', () => {
        cy.get('input[name="email"]').type('test@email.co.uk');
        cy.get('input[name="password"]').type('Letmein!{enter}');
        cy.get('[data-testid="notificationMessage"]').should(
            'contain',
            'You are logged in'
        );
    });
});
describe('Quick buy', () => {
    it('can add product to cart from category page', () => {
        cy.get('.products .item')
            .first()
            .find('.size .swatch-option')
            .first()
            .click();

        cy.get('.products .item')
            .first()
            .find('.color .swatch-option')
            .first()
            .click();

        cy.get('.products .item')
            .first()
            .find('button[type="submit"]')
            .click({ force: true });

        cy.get('.message-success')
            .invoke('text')
            .should('match', /You added (.*) to your shopping cart/);
    });
});
describe('Quick buy', () => {
    it('can add product to cart from category page', () => {
        cy.server();
        cy.route('POST', '**/checkout/cart/add/**').as('addToCart');
        cy.wait('@addToCart');

        // Run assertions
    });
});

Best Practices

Focus on strong tests

Repeatable

Independent

Use data-* attributes as selectors

<button id="main" class="btn btn-large" data-testid="submitBtn">Submit</button>
cy.get('button').click();
cy.get('.btn.btn-large').click();
cy.get('#main').click();
cy.contains('Submit').click();
cy.get('[data-testid="submitBtn"]').click();

Make A/B Tests toggle-able

beforeEach(() => {
    cy.setCookie('theme', 'a');
});

describe('Button', () => {
    it('can be clicked', () => {
        cy.get('[data-testid="buttonA"]').click();
    });
});
beforeEach(() => {
    cy.setCookie('theme', 'b');
});

describe('Button', () => {
    it('can be clicked', () => {
        cy.get('[data-testid="buttonB"]').click();
    });
});

Variant A

Variant B

Fake things sparingly

beforeEach(() => {
    cy.visit('https://demo.deity.io/customer/account/login');
    cy.login('test@email.co.uk', 'testtest123!').then(() =>
        cy.visit('https://demo.deity.io/customer/account')
    );
});

describe('Login', () => {
    it('can log user in', () => {
        cy.get('.icon-logout').should('exist');
        cy.get('.col-md-9 > h1').should('contain', 'My Dashboard');
    });
});
beforeEach(() => {
    cy.visit('https://demo.deity.io/customer/account/login');
    cy.get('input[name="email"]').type('test@email.co.uk');
    cy.get('input[name="password"]').type('testtest123!{enter}');
});

describe('Login', () => {
    it('can log user in', () => {
        cy.get('.icon-logout').should('exist');
        cy.get('.col-md-9 > h1').should('contain', 'My Dashboard');
    });
});
Cypress.Commands.add('login', (email, password) => {
    return cy.request('POST', 'https://demo.deity.io/api/customer/sign-in', {
        email,
        password
    });
});

Commands

describe('Search', () => {
    it('shows empty message when no results', () => {
        cy.get('.right-icons > [data-testid="openSearchPanel"]').click();
        cy.focused().type('product does not exist');

        cy.get('[data-testid="searchPanel"]').should(
            'contain',
            'No results were found'
        );
    });
});
describe('Search', () => {
    it('shows empty message when no results', () => {
        cy.server();
        cy.route(
            'POST',
            '**/api/catalog/vue_storefront_catalog/product/_search?**',
            {
                took: 0,
                timed_out: false,
                _shards: { total: 5, successful: 5, skipped: 0, failed: 0 },
                hits: { total: 0, max_score: null, hits: [] }
            }
        );

        cy.get('.right-icons > [data-testid="openSearchPanel"]').click();
        cy.focused().type('yoga');

        cy.get('[data-testid="searchPanel"]').should(
            'contain',
            'No results were found'
        );
    });
});

Not everything needs testing

Additional Features

  • Parallelisation
  • CI / CD Integration
  • Screenshot / Video Recordings
  • Dashboard Service
  • Install Browser Extensions

Limitations

  • Limited <iframe> support
  • Can't run multiple browsers at the same time
  • Can't mock async requests using fetch (workaround available)
  • Browser support *

Alternatives

TL;DR

Focus on real tests, tools like Cypress will help make this easier.

Thank you!

Made with Slides.com