When building a web application, you need to ensure that your components are well-tested. One of the ways to test your application is to use a library like @testing-library. This library provides you with functions to render your components and interact with them, making it easier to test your app.
One of the key elements in making your components testable is to add unique
data-testid
attributes to them. These attributes act as selectors that help the testing library identify the element you want to test. However, it’s important to avoid hardcoding these attributes in your components, as doing so might lead to false positives when testing your app.Here’s an example of a hardcoded
data-testid
:import React from 'react'; interface ButtonProps { label: string; onClick: () => void; } const Button: React.FC<ButtonProps> = ({ label, onClick }) => { return ( <button data-testid="submit-button" onClick={onClick}> {label} </button> ); }; export default Button;
Let’s test if it’s working as expected:
import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import Button from './Button'; describe('Button component', () => { it('should call onClick function when button is clicked', () => { const mockOnClick = jest.fn(); const { getByTestId } = render( <Button label="Submit" onClick={mockOnClick} /> ); const submitButton = getByTestId('submit-button'); fireEvent.click(submitButton); expect(mockOnClick).toHaveBeenCalled(); }); });
As you can see, the test passes, but if you have multiple instances of this component on a page, the hardcoded
data-testid
will lead to incorrect test results.As mentioned earlier, hardcoding
data-testid
attributes in reusable components might lead to false positives when testing your app. Take a look at this example:import React from 'react'; interface ButtonProps { label: string; onClick: () => void; } const Button: React.FC<ButtonProps> = ({ label, onClick }) => { return ( <button data-testid="submit-button" onClick={onClick}> {label} </button> ); }; const App = () => { return ( <div> <Button label="Submit 1" onClick={() => console.log('Button 1 clicked')} /> <Button label="Submit 2" onClick={() => console.log('Button 2 clicked')} /> </div> ); }; export default App;
Now, let’s write a test for this App component, where we want to check if both buttons are working as expected:
import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import App from './App'; describe('App component', () => { it('should call the onClick function of both buttons when they are clicked', () => { const mockOnClick1 = jest.fn(); const mockOnClick2 = jest.fn(); const { getByTestId } = render( <App /> ); const submitButton1 = getByTestId('submit-button'); fireEvent.click(submitButton1); expect(mockOnClick1).toHaveBeenCalled(); const submitButton2 = getByTestId('submit-button'); fireEvent.click(submitButton2); expect(mockOnClick2).toHaveBeenCalled(); }); });
In this case, even though we are using different
onClick
functions for both buttons, the test will still pass, because both buttons have the same data-testid
of submit-button
. This is a clear example of how hardcoded testid
s can lead to false positives in testing.To avoid this issue, you should pass
data-testid
attributes as props, so each component instance can have a unique value. This will help ensure that your tests are accurate and reliable. Here’s an example:import React from 'react'; interface ButtonProps { label: string; onClick: () => void; testId: string; } const Button: React.FC<ButtonProps> = ({ label, onClick, testId }) => { return ( <button data-testid={testId} onClick={onClick}> {label} </button> ); }; export default Button;
And here’s the updated test for this component:
import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import Button from './Button'; describe('Button component', () => { it('should call onClick function when button is clicked', () => { const mockOnClick = jest.fn(); const { getByTestId } = render( <Button label="Submit" onClick={mockOnClick} testId="submit-button-1" /> ); const submitButton = getByTestId('submit-button-1'); fireEvent.click(submitButton); expect(mockOnClick).toHaveBeenCalled(); }); });
With this approach, you can render multiple instances of the component and each instance will have a unique
testid
, allowing you to test each component instance individually.I hope you enjoyed it and learned something new. In that case, please share it with your friends and colleagues 😃
Cheers! 🍻