March 4, 2025

Automating React Playwright tests with data-testid

Using data-testid with Playwright makes it easy to select elements for testing. This is ideal when UI components change frequently but need consistent functionality. By using a unique data-testid for each element, tests are simpler to maintain and more stable, as these identifiers allow direct access to components, regardless of visual changes.

Why not just use id or classes?

// Using data-testid
function LoginButton() {
  return <button data-testid="login-button">Log In</button>;
}

// Using id
function LoginButton() {
  return <button id="login-button">Log In</button>;
}

While all of these are attributes on an element, there are some real benefits to using data-testid for automation:

  • data-testid indicates its use for testing, which helps it stand out from other attributes like id or class that might be used for styling or scripting
  • Since data-testid is solely for testing, your tests remain unaffected by changes to styles or scripts that use id or class for other purposes
  • data-testid can be safely removed in production. This keeps the production code clean and optimized, while also preventing easy scraping

Another benefit is the improved readability. With data-testid, you can use descriptive names that reflect the element's role. This can be added to more elements since it doesn't affect styles.

Playwright Locators

With Playwright you use locators to find elements on the page. For the full list check out Locators in the docs.

The one we're interested in is getByTestId

function LoginButton() {
  return <button data-testid="login-button">Log In</button>;
}
await page.getByTestId("login-button").click();

Configuring the testid attribute

You may already use something other than data-testid. Playwright allows configuration of this in playwright.config.ts

import { defineConfig } from "@playwright/test";

export default defineConfig({
  use: {
    testIdAttribute: "data-totallyatestid",
  },
});

This will then get picked up instead when calling getByTestId.

Example

Let's take a look at a practical example.

Suppose we had this test that relies on id selectors:

test("Should reset password", async ({ page }) => {
  await page.goto("http://localhost:3000/forgot");
  await page.locator("#email").fill(testEmailAddress);
  await page.locator("form").click();
  // this will send an email with a reset link to the provided email address

  // Example email checking code (would depend on your email testing setup)
  const emails = await mailisk.searchInbox({
    to_addr_prefix: testEmailAddress,
    subject_includes: "reset",
  });

  const email = emails[0];
  const resetLink = email.text.match(/.*\[(http:\/\/localhost:3000\/.*)\].*/)[1];
  expect(resetLink).toBeDefined();
});

If we change the component a bit and add our data-testid attribute:

return (
  <div className="flex justify-center mt-9">
    <div className="bg-gray-100 rounded-md">
      <form data-testid="password-form" ...>
        <p>We'll send a password reset link</p>
        <input
          type="text"
          id="email"
          data-testid="email-input"
          ...
        />
        ...
      </form>
    </div>
  </div>
);

We can then use these data-testid attributes directly instead:

test("Should reset password", async ({ page }) => {
  await page.goto("http://localhost:3000/forgot");
  await page.getByTestId("email-input").fill(testEmailAddress);
  await page.getByTestId("password-form").click();
  // ...
});

Using the getByTestId() method we can make our tests cleaner and more readable than CSS attribute selectors.

Removing data-testid from production

This depends on your tooling and framework. There's a plugin for every popular tool that allows the removal of custom attributes.

Let's say you're using Babel and JSX components (React). In that case, something like the babel-plugin-jsx-remove-data-test-id library will fit your needs.

No matter what you choose, make sure to look at the documentation and select the correct attribute. In this post, we mentioned data-testid, but this is by no means required, so adjust accordingly to your project.

Ready to start testing emails?
Create a free account.

Get started