End to End Testing for Web Apps

Agenda

  • What/Why
  • What to test
  • Writing a test plan
  • Choosing your tools
  • Best practices
  • Testing Environment
  • Dashboard demo

The Testing Pyramid

Unit Testing

Test individual methods or functions of the classes, components or modules used by your app. These are the least expensive tests and are the easiest to integrate into CI.

Integration Testing

Test individual units of your app as a group. Integration tests provide a balance between the speed of unit tests and the “real world” interactions of end-to-end tests.

End to End Testing

Test by replicating user behavior in a complete application environment and verify that user stories flow as expected. E2E tests bring the most confidence by validating the entire chain of system dependencies such as communicating with other systems, interfaces, databases, and networks.

Challenges with E2E Testing

Slow

It takes time to start new browser instance and load the page. It may take 20 seconds for one test, and with many tests executed in sequence, it goes up to minutes.

Hard to develop

Developing is difficult with a slow feedback loop and cryptic error messages.

Non-deterministic issues

A test is non-deterministic when it passes or fails randomly, without any noticeable change in the code or environment.

Brittle

Because these tests suffer from direct coupling with the user interface itself, they are more likely to break with the most minor UI changes.

What to Test

The Happy Path

Happy path testing reflects the most essential user stories of your product. Starting with well-defined test cases helps you focus on the core UX scenarios where bugs are unacceptable. For exception scenarios, use integration testing and low-level unit testing.

Not a lot!

Keep end to end tests to a bare minimum. Google suggests a 70/20/10 split: 70% unit tests, 20% integration tests, and 10% end-to-end tests.

Define Scope

Decide what components are in scope for your app. Is it just the front end or the whole application stack?

Writing a test plan

Analyze

Decide how you will logically divide up the app

Write out each step from a user perspective

  • Be as detailed as possible. Write out every ui interaction that a user needs to make in order to complete a scenario.
  • By view? feature?
  • Who are your users?
  • How is the app used?
  • What are the most critical features?

E2E Testing Tools

 Testing Frameworks

  • Nightwatch.js
  • Webdriver.io
  • Cypress.io
  • Testcafe

Selenium Based

Non Selenium Based

Browsers

  • Headless Firefox / Chrome
  • Browserstack
  • SauceLabs

Best Practices

Use the Page Object Model pattern

A page object is a class that serves as an interface to a page of your app. It makes tests readable and increases maintainability. If a selector is changed, you only have to update it one place, not throughout your whole test suite.

// page model

export default class BaseViewModel {
  constructor( {
    // Top bar
    this.linkFabric = `[data-test="link-fabric"]`
    this.linkBreadcrumbs = `[data-test="link-breadcrumbs"]`
    this.linkSettings = `[data-test="link-settings"]`

    // Navigation
    this.linkTabs = `[data-test="link-tabs"]`

    // Internationalization
    this.linkLanguages = `[data-test="link-languages"]`
    this.linkLanguagesEn = `[data-test="link-languagesEN"]`
    this.linkLanguagesEs = `[data-test="link-languagesES"]`

    // Banner
    this.textTitle = `[data-test="text-title"]`

    // Footer bar
    this.linkDecipher = `[data-test="link-decipher"]`
    this.linkGitHub = `[data-test="link-github"]`
    this.linkTwitter = `[data-test="link-twitter"]`
    this.linkLinkedIn = `[data-test="link-linkedin"]`
  }
}
// test file

const baseView = new BaseViewModel();

expect(baseView.textTitle).toBe("Fabric");

Best Practices

Use data attributes

data-* attributes keep the tests independent from changes in the CSS or JS (ex. button id=”main” class=”btn btn-large” data-test=”submit-login-btn”); this will make your tests more resilient.

Start each test suite from view you're testing

Always set the start url in the context of your test, this will lower the execution time of the test suites and avoid fails triggered by bugs in different parts of the application which are not to be tested by the respective test suite.

Don't sleep

Avoid waiting for arbitrary periods of time in your test. Instead use assertions that wait for a specific condition to be met; this will make the tests less flaky.

Text

Testing Environment

deck

By Kaitlin Moreno