Back to Test Automation
2026-04-065 min read

End-to-end tests (Test Automation)

Learn End-to-end tests (Test Automation) step by step with clear examples and exercises.

Why This Matters

End-to-end testing is an integral part of the software development lifecycle as it ensures seamless user experiences across various web applications. Automating these tests can save time, reduce human errors, and provide quick feedback on any issues that may arise during development or deployment. In this lesson, we will guide you through comprehensive end-to-end testing using popular tools like Selenium, Cypress, and Playwright with JavaScript examples.

By mastering the skills presented in this lesson, you'll be able to:

  1. Write robust tests that cover the entire application workflow from start to finish.
  2. Save time by automating repetitive tasks and reducing the need for manual testing.
  3. Catch bugs early in the development process, leading to faster problem resolution.
  4. Improve the overall quality of your web applications by ensuring they function as expected across different browsers and devices.

Prerequisites

To follow along with this lesson, you should have a basic understanding of:

  • JavaScript programming language
  • HTML and CSS for web development
  • Familiarity with one or more web browsers (Chrome, Firefox, Safari)
  • Node.js installed on your machine

It's also recommended to have a text editor like Visual Studio Code or Atom for writing and editing code.

Core Concept

End-to-end testing involves verifying the entire application workflow from start to finish. The tests simulate user interactions, such as clicking buttons, filling forms, and navigating through pages, to ensure that the application behaves as expected. Three popular tools for end-to-end testing with JavaScript are:

  1. Selenium: A widely used open-source tool that supports multiple programming languages and browsers. It uses WebDriver API to control web browsers programmatically.
  2. Cypress: A newer, faster, and easier-to-use end-to-end testing solution built specifically for modern JavaScript applications. It provides real-time reloading, snapshot testing, and intelligent waiting.
  3. Playwright: A Node.js library developed by Microsoft that supports Chromium, Firefox, and WebKit browsers. It offers similar features to Selenium but with improved performance and ease of use.

Benefits of End-to-End Testing

  1. Reduces human errors in testing
  2. Increases test coverage for complex applications
  3. Provides quick feedback on issues during development or deployment
  4. Ensures a consistent user experience across different browsers and devices

Worked Example

In this section, we will provide detailed examples for each tool mentioned above.

Using Selenium

First, install the necessary dependencies:

npm install selenium-webdriver chrome-webdriver

Create a new JavaScript file (e.g., selenium_example.js) and write the test code:

const {Builder, By, Key, until} = require('selenium-webdriver');

async function main() {
const driver = await new Builder().forBrowser('chrome').build();

try {
await driver.get('http://example.com/login'); // Navigate to the login page

await driver.findElement(By.id('username')).sendKeys('user123'); // Fill in the username field
await driver.findElement(By.id('password')).sendKeys('pass123', Key.RETURN); // Fill in the password field and submit

await driver.wait(until.urlIs('http://example.com/dashboard'), 10000); // Verify that we are on the dashboard page
} catch (error) {
console.error(error);
} finally {
await driver.quit();
}
}

main();

Run the test using Node.js:

node selenium_example.js

Using Cypress

First, install Cypress by running npm install cypress in your project directory.

Create a new spec file (e.g., login.spec.js) in the cypress/integration folder. Write the test code inside the spec file:

describe('Login page', () => {
it('should allow valid login', () => {
cy.visit('http://example.com/login') // Navigate to the login page

cy.get('#username').type('user123') // Fill in the username field
cy.get('#password').type('pass123') // Fill in the password field
cy.get('#submit-button').click() // Click the submit button

cy.url().should('include', '/dashboard') // Verify that we are on the dashboard page
})
})

Run the test by executing npm run cypress:run in your terminal.

Using Playwright

First, install Playwright using npm:

npm i playwright

Create a new JavaScript file (e.g., playwright_example.js) and write the test code:

const { chromium } = require('playwright');

(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();

try {
await page.goto('http://example.com/login'); // Navigate to the login page

await page.fill('#username', 'user123'); // Fill in the username field
await page.fill('#password', 'pass123'); // Fill in the password field
await page.click('#submit-button'); // Click the submit button

await page.waitForURL('http://example.com/dashboard'); // Verify that we are on the dashboard page
} catch (error) {
console.error(error);
} finally {
await browser.close();
}
})();

Run the test using Node.js:

node playwright_example.js

Common Mistakes

  • Forgetting to wait for elements before interacting with them
  • Not handling errors properly during tests
  • Ignoring asynchronous operations and timeouts
  • Writing brittle tests that fail due to minor UI changes

1. Waiting for Elements

To avoid issues caused by elements not being immediately available, use Cypress' wait() or should() functions to ensure they are loaded before interacting with them:

cy.get('#username').should('be.visible') // Wait until the username field is visible

2. Handling Errors

Use Cypress' try...catch block to handle errors during tests:

try {
cy.get('#non-existent-element').click()
} catch (error) {
expect(error).to.not.exist // Verify that no error occurred
}

3. Asynchronous Operations and Timeouts

When dealing with asynchronous operations, use Cypress' wrap() function to ensure the test waits for the operation to complete:

cy.wrap(new Promise((resolve, reject) => {
setTimeout(() => resolve('Result'), 1000) // Simulate an asynchronous operation
}).then(result => expect(result).to.equal('Result')))

Practice Questions

  1. Write an end-to-end test for a registration form using Selenium.
  2. Implement a test to verify the functionality of a search bar using Playwright.
  3. How would you handle a scenario where the login page requires CAPTCHA verification?
  4. What are some best practices for writing maintainable end-to-end tests?

FAQ

1. What is the difference between unit tests and end-to-end tests?

Unit tests focus on individual functions or methods, while end-to-end tests cover the entire application workflow from start to finish. Unit tests are faster and more efficient for catching errors in smaller code segments, whereas end-to-end tests provide a broader view of the application's functionality.

2. Can I use Cypress with React applications?

Yes, Cypress integrates well with modern JavaScript frameworks like React. To set up a test environment for a React app, follow the official Cypress documentation: https://www.cypress.io/docs/guides/getting-started-with-react

3. How can I improve the performance of my end-to-end tests?

To optimize the performance of your end-to-end tests, consider using headless browsers, reducing test suite size, and implementing parallel testing for faster execution times. Additionally, use intelligent waiting functions to minimize wait times between actions.

4. What are some best practices for writing maintainable end-to-end tests?

  1. Write clear and concise test descriptions that explain the expected behavior.
  2. Use data-driven testing to reduce duplication and make tests more flexible.
  3. Keep tests independent of each other to avoid dependencies and ensure faster execution times.
  4. Implement a robust test setup and teardown process to minimize test flakiness.
  5. Regularly refactor tests to maintain their relevance as the application evolves.
End-to-end tests (Test Automation) | Test Automation | XQA Learn