25

I'm building a React app with TypeScript. I do my component tests with react-testing-library.

I'm buildilng a parallax component for my landing page.

The component is passed the image via props and sets it via JSS as a background image:

<div
  className={parallaxClasses}
  style={{
    backgroundImage: "url(" + image + ")",
    ...this.state
  }}
>
  {children}
</div>

Here is the unit test that I wrote:

import React from "react";
import { cleanup, render } from "react-testing-library";
import Parallax, { OwnProps } from "./Parallax";
afterEach(cleanup);

const createTestProps = (props?: object): OwnProps => ({
  children: null,
  filter: "primary",
  image: require("../../assets/images/bridge.jpg"),
  ...props
});

describe("Parallax", () => {
  const props = createTestProps();
  const { getByText } = render(<Parallax {...props} />);
  describe("rendering", () => {
    test("it renders the image", () => {
      expect(getByText(props.image)).toBeDefined();
    });
  });
});

But it fails saying:

● Parallax › rendering › it renders the image

    Unable to find an element with the text: bridge.jpg. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

    <body>
      <div>
        <div
          class="Parallax-parallax-3 Parallax-primaryColor-4"
          style="background-image: url(bridge.jpg); transform: translate3d(0,0px,0);"
        />
      </div>
    </body>

      16 |   describe("rendering", () => {
      17 |     test("it renders the image", () => {
    > 18 |       expect(getByText(props.image)).toBeDefined();
         |              ^
      19 |     });
      20 |   });
      21 | });

      at getElementError (node_modules/dom-testing-library/dist/query-helpers.js:30:10)
      at getAllByText (node_modules/dom-testing-library/dist/queries.js:336:45)
      at firstResultOrNull (node_modules/dom-testing-library/dist/query-helpers.js:38:30)
      at getByText (node_modules/dom-testing-library/dist/queries.js:346:42)
      at Object.getByText (src/components/Parallax/Parallax.test.tsx:18:14)

How can I test that the image is being set as a background image correctly with Jest and react-testing-library?

J. Hesters
  • 8,261
  • 15
  • 78
  • 164

2 Answers2

39

getByText won't find the image or its CSS. What it does is to look for a DOM node with the text you specified.

In your case, I would add a data-testid parameter to your background (<div data-testid="background">) and find the component using getByTestId.

After that you can test like this:

expect(getByTestId('background')).toHaveStyle(`background-image: url(${props.image})`)

Make sure you install jest-dom in order to have toHaveStyle.

Giorgio Polvara - Gpx
  • 12,268
  • 5
  • 53
  • 55
  • 1
    This is not working for me, I get `TypeError: expect(...).toHaveValue is not a function` even with jest-dom installed. Could it be because I'm testing a styled-component? – Luiz Henrique Guerra Jan 13 '20 at 22:45
  • 1
    Got it, had to add `import '@testing-library/jest-dom/extend-expect'` to the test file. Found it here https://spectrum.chat/testingjavascript/help/where-do-i-set-up-jest-dom-extend-expect~ea744f78-a306-465a-a599-4abad4bc1857 – Luiz Henrique Guerra Jan 13 '20 at 22:54
  • I really suggest you will make a setupTests file which import this to any jest file you use :) and than all of the "toHaveStyle, Content, etc...." will auto import. at any test file. keep the development faster and solve issues you may have when you forgot to import it. – Ido Bleicher Aug 16 '20 at 14:09
  • @LuizHenriqueGuerra your error msg says: toHaveValue The answer says toHaveStyle – Brainmaniac Oct 20 '20 at 13:40
  • @Brainmaniac either way none of the methods from the expect api were working without the import in my next comment – Luiz Henrique Guerra Nov 27 '20 at 01:50
7

If you want to avoid adding data-testid to your component, you can use container from react-testing-library.

const {container} = render(<Parallax {...props})/>
expect(container.firstChild).toHaveStyle(`background-image: url(${props.image})`)

This solution makes sense for your component test, since you are testing the background-image of the root node. However, keep in mind this note from the docs:

If you find yourself using container to query for rendered elements then you should reconsider! The other queries are designed to be more resiliant to changes that will be made to the component you're testing. Avoid using container to query for elements!

user56reinstatemonica8
  • 27,132
  • 16
  • 87
  • 109
Katie McCulloch
  • 146
  • 1
  • 5
  • 1
    One way to avoid the problem of making the implementation-detail assumption that it's the container's first child that carries the background image might be to give the element with the background image an aria-role of an image (`role="img"`) and then use `getByRole('img')` instead of `container.firstChild`. Then the tester looks up the tree for the first element that acts as an image. – user56reinstatemonica8 Jan 02 '20 at 11:12