Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React testing library: Test attribute / prop

I'm writing a React app using TypeScript. I use material-ui for my components and react-testing-library for my unit tests.

I'm writing a wrapper for material-ui's Grid component so that I always have an item.

import Grid from "@material-ui/core/Grid";
import withStyles, { WithStyles } from "@material-ui/core/styles/withStyles";
import React, { PureComponent } from "react";
import styles from "./styles";

export interface OwnProps {
  className?: string;
}

export interface Props extends WithStyles<typeof styles>, OwnProps {}

export interface DefaultProps {
  className: string;
}

export class GridItem extends PureComponent<Props & DefaultProps> {
  static defaultProps: DefaultProps = {
    className: ""
  };

  render() {
    const { classes, children, className, ...rest } = this.props;
    return (
      <Grid
        data-testid="grid-item"
        item={true}
        {...rest}
        className={classes.grid + " " + className}
      >
        {children}
      </Grid>
    );
  }
}

export default withStyles(styles)(GridItem);

I want to write a unit test that checks if item={true}. I tried to use the helper library jest-dom's toHaveAttribute like this:

import "jest-dom/extend-expect";
import React from "react";
import { cleanup, render } from "react-testing-library";
import GridItem, { OwnProps } from "./GridItem";
afterEach(cleanup);

const createTestProps = (props?: object): OwnProps => ({
  ...props
});

describe("Parallax", () => {
  const props = createTestProps();
  const { getByTestId } = render(<GridItem {...props} />);
  describe("rendering", () => {
    test("it renders the image", () => {
      expect(getByTestId("grid-item")).toHaveAttribute("item", "true");
    });
  });
});

But this test fails with:

● GridItem › rendering › it renders the image

    expect(element).toHaveAttribute("item", "true") // element.getAttribute("item") === "true"

    Expected the element to have attribute:
      item="true"
    Received:
      null

      14 |   describe("rendering", () => {
      15 |     test("it renders the image", () => {
    > 16 |       expect(getByTestId("grid-item")).toHaveAttribute("item", "true");
         |                                        ^
      17 |     });
      18 |   });
      19 | });

      at Object.toHaveAttribute (src/components/Grid/GridItem/GridItem.test.tsx:16:40)

Test Suites: 1 failed, 3 passed, 4 total
Tests:       1 failed, 3 passed, 4 total
Snapshots:   0 total
Time:        1.762s, estimated 2s
Ran all test suites related to changed files.

How can I test if an element has a certain attribute?

like image 223
J. Hesters Avatar asked Nov 03 '18 15:11

J. Hesters


People also ask

How do I get the element by type in React testing library?

To find elements by className in React testing library: Render a component and destructure the container object from the result. Use the getElementsByClassName() method on the container to find elements by class name.

What does getByTestId return?

getByTestId does return the first item with that test ID in the virtual DOM. I am able to get all of the test items by using the test renderer directly and doing a findAllByProp query from there.

How do you use data Testid in React testing library?

Where do we put data-testid ? To enable test automation support in applications using a React testing library, we need to provide a special data-testid attribute for each component. It must be placed on the containing element of each section and all related subsections.

How to update props of a rendered component in react testing library?

If you wish to update the props of a rendered component in your React testing library test, then you can simply use the rerender function like so: In this way, you won't be remounting the component.

What is the react testing library?

The React Testing Library is a very light-weight solution for testing React components. It provides light utility functions on top of react-dom and react-dom/test-utils, in a way that encourages better testing practices. Its primary guiding principle is:

How to find an element by specific attributes in react testing library?

Most of your React test cases should use methods for finding elements. React Testing Library provides you with several methods to find an element by specific attributes in addition to the getByText () method above: getByText (): find the element by its textContent value getByRole (): by its role attribute value

How do I write maintainable tests for my React components?

You want to write maintainable tests for your React components. As a part of this goal, you want your tests to avoid including implementation details of your components and rather focus on making your tests give you the confidence for which they are intended.


2 Answers

jest-dom toHaveAttribute assertion asserts item attribute while the test tries to test item prop.

item prop won't necessarily result in item attribute, and since it's non-standard attribute it most probably won't.

react-testing-library propagates functional testing and asserts resulting DOM, this requires to be aware of how components work. As can be seen here, item props results in adding a class to grid element.

All units but tested one should be mocked in unit tests, e.g.:

...
import GridItem, { OwnProps } from "./GridItem";

jest.mock("@material-ui/core/Grid", () => ({
  default: props => <div data-testid="grid-item" className={props.item && item}/>
}));

Then it could be asserted as:

  expect(getByTestId("grid-item")).toHaveClass("item");
like image 105
Estus Flask Avatar answered Sep 28 '22 11:09

Estus Flask


If someone is still having this issue I solved it this way:

it('Check if it is a materialUI Grid item', () => {
    //Rendering the component in a constant.
    const { container } = render(<YourComponent />); 
    //Accessing the grid wrapper. In this case by the attribute you provided.
    const grid = container.querySelector('[data-testid="grid-item"]'); 
    //What we are expecting the grid to have.  
    expect(grid).toHaveClass('MuiGrid-item');
})

Notes:

  1. I noticed that in the code item it's been declared as a string and not as a boolean: item='true', which will trigger a warning when you run the test. item={true} is the correct way of declaring it. Actually in material UI when you write item inside a grid its of course by default true, in my opinion is not necessary.
  2. item is a class inside material UI Grid as the previous answer correctly suggested. So by that the correct class name should be refered in this case is 'MuiGrid-item'.
like image 27
Manuel Barbo Avatar answered Sep 28 '22 12:09

Manuel Barbo