How to test if a component is rendered with the right props when using react-testing-library?
Asked Answered
K

4

49

I have some components that are rendering another component (FetchNextPageButton) that is already tested in isolation, like these ones:

const News = () => (
  <div>
    <h1>News</h1>
    ...
    <FetchNextPageButton query={NEWS_QUERY} path="viewer.news" />
  </div>
)

const Jobs = () => (
  <div>
    <h1>Jobs</h1>
    ...
    <FetchNextPageButton query={JOBS_QUERY} path="viewer.jobs" />
  </div>
)

const Posts = () => (
  <div>
    <h1>Posts</h1>
    ...
    <FetchNextPageButton query={POSTS_QUERY} path="viewer.posts" />
  </div>
)

The thing is that I'd not like having to add tests on each of these components for a functionality that is already tested somewhere else, so I think that should be enough just to test that the component is rendered and that I'm passing the right props to it.

I'd have been able to test this easily with Enzyme with something like this:

expect(wrapper.find('FetchNextPageButton').props()).toMatchObject({
  query: NEWS_QUERY,
  path: "viewer.news"
})

So I'm wondering what's the best approach to test it by using React testing library instead.

Kittrell answered 30/10, 2019 at 10:47 Comment(0)
K
64

This is the approach that Kent C. Dodds (the creator of RTL) shared with me after discussing it with him:

import FetchNextPageButton from 'FetchNextPageButton'

jest.mock('FetchNextPageButton', () => {
  return jest.fn(() => null)
})

// ... in your test
expect(FetchNextPageButton).toHaveBeenCalledWith(props, context)
Kittrell answered 30/10, 2019 at 22:16 Comment(7)
@AlexMckay we discussed it in this twitter thread: twitter.com/kentcdodds/status/1189662486007468032Kittrell
Am I the only one this did not work with? when I ran it states: Matcher error: received value must be a mock or spy function Received has type: function Received has value: [Function Component]Overby
@Overby You have to mock the component first, then pass it to the assertion.Uncertainty
note: this approach is NOT recommended by Kent C. Dodds. He wrote the code snippet that shows how to do it, but said he does not recommend the approach (this is also in the twitter thread). Similar question hereSecretive
@Secretive I understood that he doesn't recommend to do it as a rule of thumb, but in case you have to do it (there might be good reasons to test in isolation, he says), that's how he actually does it.Kittrell
@Uncertainty Can you elaborate. I don't understand. And what is "context" in this case.Teaspoon
If you don't care about the context (probably you don't), you can add expect.any() or an empty object {}, like expect(FetchNextPageButton).toHaveBeenCalledWith(props, {}).Kittrell
U
10

Don't believe it's possible. RTL looks like focusing on validating against DOM not React's components tree.

The only workaround I see is to mock FetchNextPageButton to make it rendering all props into attributes.

jest.mock("../../../FetchNextPageButton.js", () => 
  (props) => <div data-test-id="FetchNextPageButton" {...props} />);
....
const { getByTestId } = render(<YourComponent />);
expect(getByTestId("FetchNextPageButton")).toHaveAttribute("query", NEWS_QUERY);
expect(getByTestId("FetchNextPageButton")).toHaveAttribute("path", "viewer.news");

Sure, this is smoothly only for primitive values in props, but validating something like object or function would be harder.

Think, it's not RTL-way, but I agree it would be massive work to check that in scope of each container(and completely ignoring that would be rather a risk).

PS toHaveAttribute is from jest-dom

Uranography answered 30/10, 2019 at 11:4 Comment(2)
This will require all the props to be in lowercase which kinda restricting, as render or act functions will throw a warning of unrecognized props.Effortless
@KhalifaGad yes, that's true. And you always can write more complex mocks if you don't like to see warnings.Uranography
Z
3

In my case, I wanted to test that a Higher Order Component (HOC), correctly enhances the component that is passed to the HOC.

What I needed to do, is make the actual component a mock and pass it to the HOC. Like described in the existing answer, you can then just expect the properties, added by the HOC.

// after 'Component' get's passed into withSelectionConstraint, it should have an id prop
const Component = jest.fn(() => <h1>Tag Zam</h1>);
const WithConstraint = withSelectionConstraint(Component, ["instance"], true);
render(<WithConstraint />);

// passing the jest mock to the HOC, enables asserting the actual properties passed by the HOC
expect(Component).toHaveBeenCalledWith(
    expect.objectContaining({ ids: mockInstanceRows.map(x => x.id) }), 
    expect.anything()
)


Zigmund answered 31/3, 2022 at 9:46 Comment(0)
O
0

Based on Ben's answer, I wrote a version which doesn't raise any error :

jest.mock(
  'path/to/your/component',
  () => {
    const MockedComponent = (props: any) => {
      const cleanedProps = Object.keys(props).reduce<Record<string, unknown>>(
        (a, b) => {
          // Needed because html attributes cannot be camel cased
          a[b.toLowerCase()] = props[b].toString();
          return a;
        },
        {}
      );

      return (
        <div data-testid="any-test-id" {...cleanedProps} />
      );
    };

    return MockedComponent;
  }
);

Note that the attributes values (expect(getByTestId('any-test-id')).toHaveAttribute('attribute','value')) will be stringified.

Olivenite answered 25/10, 2022 at 12:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.