Reputation: 21
I'm trying to create a unit test for testing a React component with fat arrow function methods. I'm trying to mock these methods in the unit test via react-testing-library + jest, but with no success
I was not able to do the mocking as the jest.spyOn throws an error saying that the method is not a function
Upvotes: 2
Views: 711
Reputation: 5900
You cannot spy on a react component function. I was also trying to make that work. I wanted to be sure, so I wrote a few simplistic components and tests, and tried all possible configurations, including named versus default exports and a little import * as Component
so one can then do const spy = jest.spyOn(Component, 'default')
. It. Doesn't. Work. I would love to be wrong about this, but after all that I read today, I do not think so. This appears to be intentional on the part of RTL, and exactly for the reason explained by @dangerismycat.
You can however, auto-mock a component like so:
jest.mock('./path-to-component')
Or if you want to mock only some of the named exports, you could write:
const mockExport = jest.fn()
jest.mock('./path-to-component', () => {
const actual = jest.requireActual('./path-to-component')
return {
...actual,
namedExport: mockExport,
}
})
You can even do this with default exports, though there are caveats.
Between auto-mock v. spy approaches, one notable difference is that the auto-mocked component cannot be restored to it's original pre-mocked state like a spy can. A spy can also stay true to its name, only spying on the normal functioning of the component for use with assertions. It can also be chained with a .mockImplementation
, or .mockReturnValue
, etc. With a working spy you can do all of these things in the same file; just not with a modern react function component.
The auto-mock does have some cool features, as illustrated by the snippets above, and certainly more I have yet to discover. Hoisting the auto-mocks above the imports in particular is an interesting touch. Without a doubt mocks are an essential tool for anyone working on a massive application.
While it is not technically difficult to understand the argument for writing tests from a user perspective, and it has fundamentally changed the way I think about writing tests as a result of this quandary, this is still an unnecessary limitation. There can be thousands of files in one single application if it lives long enough, and the redux store would be totally bloated in an app like that. There may be remnants of enzyme, too, and plenty of class components! In that scenario, so much boilerplate is required just to test a simple behavior, and mocks are indispensable with managing that.
Furthermore, there is another fun trend cropping up where components are wrapped with a container that is responsible for massaging all of its data into displayable dates and strings and such. This Separation Of Concerns™ is quite nice. The main component is only responsible for keeping track of showing (or hiding) different elements under certain conditions. Unfortunately, the tight coupling between them conflicts with RTL's dictation of how one should test react.
Finally, there is one more aspect of this approach which feels unnecessarily restrictive. There is more at work in an application than what the user can see, so this user-only perspective seems shortsighted.
In conclusion, I recommend not fighting the opinionated framework. Like a casino, the framework always wins.
Upvotes: 0
Reputation: 854
This is breaking the encapsulation philosophy behind react-testing-library. You shouldn't be concerned with mocking internal methods; instead, focus on UI changes the user can observe, and test for those.
Check out the Guiding Principles in the docs for more context.
Upvotes: 2