I have a React project set up like this:
It is a simple application. The Dashboard
has a UserListContainer
, containing a UserList
, which lists four users with their ID and name. The UserList gets the Users from Data.ts
The application itself works just fine and displays the four users. But as soon as I try to test the UserList
with enzymes shallow rendering, the tests give me the following error message:
Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined
at invariant (node_modules/invariant/invariant.js:40:15)
at wrapWithConnect (node_modules/react-redux/lib/components/connectAdvanced.js:97:33)
at Object.<anonymous> (src/Users/UserListContainer.tsx:4:34)
at Object.<anonymous> (src/Users/index.ts:1:1)
at Object.<anonymous> (src/Dashboard/Dashboard.tsx:2:1)
at Object.<anonymous> (src/Dashboard/index.ts:1:1)
at Object.<anonymous> (src/Users/UserList.tsx:2:1)
at Object.<anonymous> (src/Users/__tests__/UserList.test.tsx:3:1)
The problem is basically that, even though we don't use the Dashboard when rendering the UserList
shallowly, React still tries to build it. I guess that happens because we access Data
through the Dashboard
index, so React will also try to resolve Dashboard
and its imports, namely UserListContainer
, because they are exported through the same index file. When I import the users directly instead of through the index, the problem disappears.
We fixed this issue by breaking the cyclic dependency but if I encounter the error again, I want to know other ways to fix it. I would also like to understand why the web application still seems to be working just fine, while the tests fail.
Also, is there a way to prevent React from resolving the imports and exports when using enzymes shallow rendering?
test("reproduce the problem", () => {
const wrapper = shallow(<UserList />)
console.log(wrapper)
expect(1).toBe(1)
})
import { Data } from "../Dashboard"
export const UserList: React.FC = () => (
<React.Fragment>
{Data.users.map(user => (
<div>
<code>{user.id} - </code>
<code>{user.name}</code>
</div>
))}
</React.Fragment>
)
export { Dashboard } from "./Dashboard" // not used but still resolved
export { Data } from "./Data" // actually used
export const Data = {
users: [
{ id: "user1", name: "Albert" },
{ id: "user2", name: "Bertha" },
{ id: "user3", name: "Chloe" },
{ id: "user4", name: "Doug" }
]
}
import { UserListContainer } from "../Users"
export const Dashboard: React.FC = () => {
return <UserListContainer />
}
import { UserList } from "./UserList"
export const UserListContainer = connect()(UserList)
One way of fixing it would be to reorder the imports in your dashboard file:
export { Data } from "./Data" // actually used
export { Dashboard } from "./Dashboard" // not used but still resolved
Web application will work in most cases because it will start resolving from your index.tsx file (or whatever your entry file name is) and go from there. Jest on the other hand starts from your test file and only resolves those imports (you can find a nice explanation why this happens here: https://railsware.com/blog/how-to-analyze-circular-dependencies-in-es6/).
We had similar problems in our projects and unfortunatelly except reordering imports and better structuring your files there is no other solution.
One "hack" that you can also do is to add:
import 'problematic_module'
to your jest setupFilesAfterEnv. That way module will be resolved before each test (but i recommend this only as a last resort).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With