A Field Guide to Front-End Test Types

Testing front-end code often feels like a binary choice: you either love it or you avoid it. The confusion usually starts with the sheer number of test types available. Unit, integration, end-to-end, accessibility, visual regression, and performance tests each serve a distinct purpose and use different tooling. Knowing which test to reach for in a given situation matters more than trying to use all of them at once.

At its core, software testing is an automated investigation that provides information about the quality of a product. Front-end testing, specifically, validates that what users see and interact with works as intended — for example, confirming that clicking a "Delete" button removes an item from the screen, without verifying whether the item was removed from the database. That kind of back-end behavior falls outside the front-end testing scope.

Unit Testing: The Building Blocks

Level: Low
Scope: Functions and methods
Common tools: AVA, Jasmine, Jest, Karma, Mocha

Unit tests are the smallest and most fundamental test type. They examine individual components and functions to ensure they produce expected outputs. This granular level of testing also handles edge cases — scenarios like a missing parameter that might be overlooked during development.

A simple example: a sayHello function that takes a name argument and returns Hello ${name}!. Testing such a function may seem trivial, but it forces you to think through possible outcomes. What happens if the name is missing? Should the function return a fallback? These edge cases matter because other parts of the codebase might depend on them, and a unit test provides a warning if someone removes critical logic later.

function sayHello(name) {
  if (name) {
    return `Hello ${name}!`;
  } else {
    return "Hello human!";
  }
}

Frameworks like Jest use describe and it blocks as syntactic sugar to organize test output in the terminal. The core assertion logic relies on functions like expect (which accepts the input to validate) and toEqual (which specifies the desired output). Jest displays the function name as a title in the terminal, with each it block reported beneath it for easy reading.

describe("sayHello", () => {
  it("returns a greeting that says hello", () => {
    expect(sayHello("Evgeny")).toEqual("Hello Evgeny!");
  });
});

Unit tests also excel at testing APIs through mocked or hardcoded data rather than live API calls, ensuring test runs remain consistent. This approach works particularly well because unit tests focus on the function's behavior, not the data source.

Integration Testing: Making Pieces Work Together

Level: Medium
Scope: Interactions between units
Common tools: AVA, Jest, Testing Library

Applications are rarely composed of isolated, self-contained components. Integration tests verify that these units work together correctly. These tests extend beyond individual functions to include the HTML DOM and user interactions like button clicks.

Consider a React application where clicking a button triggers a <Greeting /> component to display a message. An integration test renders the component in an emulated DOM (avoiding the performance cost of touching a real one), queries specific elements via test IDs, and then simulates user actions. The test flow might look like this:

  • Render the <Greeting /> component.
  • Verify the <p> element is initially empty.
  • Simulate a click event on the button with ID #show-greeting-button.
  • Assert that the <p> element now contains the expected greeting text.

Integration tests commonly use test IDs (like #greeting) because they provide a straightforward way to select specific components from the DOM, though other query methods exist. The terminal output for these tests closely resembles what you'd see in unit tests, with describe and it blocks organizing results.

End-to-End Testing: Real Browser, Real Interactions

Level: High
Scope: User interactions in a real-life browser
Common tools: Cypress, Puppeteer

End-to-end tests operate at the highest level of abstraction. They care only about how users see and interact with an application, completely unaware of the underlying implementation. These tests provide a browser with instructions — what to click, what to type, and where to navigate — then verify expected outcomes against real DOM and real data.

While unit and integration tests provide solid coverage, users can still encounter unexpected behavior in a real browser environment. E2E tests catch those issues by simulating complete user journeys. For instance, using Cypress, a test might:

  1. Use cy.visit to navigate to a URL where the application is hosted.
  2. Click a navigation button to reach a page containing the <Greeting /> component.
  3. Type a name into a text input field.
  4. Click the button that triggers the greeting.
  5. Assert that the correct greeting appears on the screen.

These commands closely mirror integration test syntax — with cy.visit and cy.get replacing their emulated-DOM counterparts — but execute in an actual browser. Watching an E2E test run at normal speed shows how rapidly these interactions happen; slowing the test down reveals each step as a robotic user clicks through the application.

Accessibility Testing: Inclusive by Default

Level: High
Scope: Compliance with accessibility standards
Common tools: AccessLint, axe-core, Lighthouse, pa11y

Accessibility testing ensures that people with disabilities can effectively use a website. Screen readers, for example, parse a site's structure and present it audibly to users. These tests verify that the code follows standards that make such assistive technologies work correctly.

Many accessibility testing tools are readily available. Chrome's DevTools includes Lighthouse, which provides an "Accessibility" testing option. Running the test in Lighthouse against the prior Greeting application takes minimal effort:

  1. Open Chrome DevTools.
  2. Select the "Accessibility" audit option.
  3. Click "Generate" to run the report.

The generated report includes a score, an audit summary, and opportunities for improvement. It's worth noting that different tools measure accessibility from different angles, so having a testing plan that covers multiple aspects of accessibility is beneficial.

Visual Regression Testing: Catching Visual Breaks

Level: High
Scope: Visual structure and appearance
Common tools: Cypress, Percy, Applitools

Changes to a codebase sometimes break the visual layout of an application in ways that E2E tests miss. Visual regression testing addresses this by capturing screenshots of pages or components and comparing them against screenshots from successful test runs. Any discrepancies trigger a notification.

Tooling like Percy integrates with Cypress, allowing a one-line addition to an existing E2E test flow: cy.percySnapshot(). This command captures a screenshot and sends it to Percy for comparison against the baseline. After tests complete, you receive a link to review any detected visual differences. In a typical scenario, the report shows the current screenshot, the baseline, and any diff highlighting precisely where the layout broke.

Performance Testing: Measuring Speed and Stability

Level: High
Scope: Performance and stability
Common tools: Lighthouse, PageSpeed Insights, WebPageTest, YSlow

Performance testing measures how quickly an application loads and runs. Given the SEO implications of Core Web Vitals, monitoring performance regressions has become increasingly important. These tests can measure initial bundle size, load time, or the speed of specific functions.

There are multiple ways to approach performance testing. Some teams establish a "performance budget" and run automated tests that fail deployments when the bundle exceeds a size threshold. Others run manual checks using Lighthouse, which also measures performance metrics through its DevTools audit. Lighthouse's integration with Core Web Vitals and its availability without installation or configuration make it an accessible starting point for performance measurement.

Choosing a Testing Strategy

The various test types described above cover different aspects of your application at different levels. A single test type rarely suffices for a production-ready front-end. The key isn't to implement each type across the board — it's recognizing which tests benefit your specific development workflow.

Some testing services require code integration, while others need little more than a URL and a configuration click. Given the breadth of available libraries and tools — not to mention their varying complexity and setup requirements — there's an accessible entry point for every skill level and budget.

Bugs caught after deployment are expensive in both user trust and engineering time. Making testing a core workflow component — whether automated as part of a continuous integration pipeline or performed manually at regular intervals — reduces that risk while contributing to a more reliable, inclusive, and efficient application.