How to use testId in React

October 8, 2022

How to use testId in React

Intent

data-testid is an attribute used to identify a DOM node for testing purposes. It should be used as a handler to the test code. We need to make sure its value is unique. Therefore, we do not cause conflicts between components. testId is a convenient way to do the React Component Unit Test. When we use Testing Library, getByTestId is useful to find the target element within DOM and Component.

Problems

When we write unit tests for a React Component, getByTestId is an easy place to start. To successfully run a test, we simply add a data-testid attribute to the element in the Component. Then in the test, we query it, run the test and assert the value or behaviour.

Component

interface Props {
  name: string;
  description: string;
}
const Product = ({ name, description }: Props) => {
  return (
    <div data-testid="Product">
      <h2 data-testid="Product-Name">{name}</h2>
      <p data-testid="Product-Description">{description}</p>
    </div>
  );
};
export default Product;

Test

describe("Product Component", () => {
  it("Should display product information", () => {
    render(
      <Product
        name="React"
        description="A JavaScript library for building user interfaces"
      />
    );
    let element = screen.getByTestId("Product-Name");
    expect(element).toHaveTextContent("React");
    element = screen.getByTestId("Product-Description");
    expect(element).toHaveTextContent(
      "A JavaScript library for building user interfaces"
    );
  });
});

There are problems that we need to consider:

  • After the application has been released to production, when we look at at the webpage HTML elements, we will find the data-testid and its value is in the HTML. For some projects, it is not a problem, but how can we ensure that the developer has not used a sensitive value for the id? From the Clean Code perspective, data-testid should be only used in Unit Test, why it is leaking into production?
<div class="App">
    <div data-testid="Product">
        <h2 data-testid="Product-Name">React</h2>
        <p data-testid="Product-Description">A JavaScript library for building user interfaces</p>
    </div>
</div>
  • testid make the Unit Test easier but when we do end to end test or integration testing, if there is hard code data-testid exist, how can forbidden the developer or testing using that id to test it? We should aim to test them in the same way that end users would interact with them. Should not have testid exist when we do the test except Unit Test
  • We don't know where the React Component is going to be used, it can be used in any where. If we put this Product Component in a parent Component or in a Array mapping, we cannot provide the testid to the Product Component, because the testid was hard code within the Component.

Solution

Component

interface Props {
  name: string;
  description: string;
  testId?: string;
}
const Product = ({ name, description, testId }: Props) => {
  return (
    <div data-testid={testId}>
      <h2 data-testid={testId ? testId + "Name" : undefined}>{name}</h2>
      <p data-testid={testId ? testId + "Description" : undefined}>{description}</p>
    </div>
  );
};
export default Product;

Test

describe("Product Component", () => {
  it("Should display product information", () => {
    const testId = "P1";
    render(
      <Product
        name="React"
        description="A JavaScript library for building user interfaces"
        testId={testId}
      />
    );
    let element = screen.getByTestId(testId + "-Name");
    expect(element).toHaveTextContent("React");
    element = screen.getByTestId(testId + "-Description");
    expect(element).toHaveTextContent(
      "A JavaScript library for building user interfaces"
    );
  });
});

Production Html Element

<div><h2>React</h2><p>A JavaScript library for building user interfaces</p></div>

When we need to release application to production, there is no test provided, so end user cannot find the data-testid attribute and the automation end to end testing tools cannot use the testid.

Summary

  • Pass testId through props as optional
  • Only use testId in Unit Test, NOT use testId in integration or end to end test.