Because enzyme
is not maintained and does not support react 18, I am trying to migrate 1750+ existing unit tests to react-testing-library
global- jsdom
to run so our application can continue to run the latest version of react. All of our tests are written using mocha
, chai
, enzyme
and I want to make migration as easy as possible. In other words, I would never rewrite 1750+ tests in a completely new framework like jest
.
I'm trying to follow the example of using react-testing-library
to unit test a react component. If I use simple elements like 'div'
or 'input'
when using React.createElement
it works fine, but when I use When using material UI components, an error occurs:
TypeError: Cannot read property of null (read "registered") In C:\Users\user\Documents\project\node_modules@emotion\styled\base\dist\emotion-styled-base.cjs.dev.js:143:53
The above error occurred in the <Styled(div)> component:
at C:\Users\user\Documents\project\node_modules\@emotion\react\dist\emotion-element-b63ca7c6.cjs.dev.js:43:23 at Box (C:\Users\user\Documents\project\node_modules\@mui\system\createBox.js:29:41) at DummyComponent (C:\Users\user\Documents\project\act-app\src\component\page\dummyComponent.js:2:346)Consider adding error boundaries to your tree to customize error handling behavior. Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries. The stack trace is not accurate, but it fails when I try to do
h(Box, {},...)
(create mui element).
This is the dummyComponent.js
I'm trying to render:
const { Box } = require('@mui/material'); const React = require('react'); const h = React.createElement; const DummyComponent = (props) => { const { children } = props; const [showChild, setShowChild] = React.useState(false); return ( h(Box, {}, h('label', { htmlFor: 'toggle' }, 'Show/Hide'), h('input', { id: 'toggle', type: 'checkbox', onChange: (e) => setShowChild(e.target.checked), checked: showChild }), showChild && children) ); }; module.exports = DummyComponent;
This is the mocha unit test:
const React = require('react'); const { render, fireEvent } = require('@testing-library/react'); const h = React.createElement; const DummyComponent = require('./dummyComponent'); describe('pageCenter', function () { before(function () { this.jsdom = require('global-jsdom')(); }); after(function () { this.jsdom(); }); it('should render', function () { const w = render(h(DummyComponent, {}, 'Hi!')); w.queryAllByText('Hi!').should.have.length(0); fireEvent.click(w.getByLabelText('Show/Hide')); w.queryAllByText('Hi!').should.have.length(1); }); });
It feels like I'm missing some context to allow the MUI component to render, but I can't seem to figure out what, or if this is actually the issue. There aren't many Google results for this specific error. Any ideas?
P粉7454121162024-01-17 00:03:09
According to the dependencies of the MUI library, I found that they use @emotion/react and @emotion/styled in some rendering processes, which seems to be an issue with MUI v5 , they changed the cache object and accidentally deleted the cache object provided by @emotion/react, which resulted in a TypeError: Cannot read property 'registered' of undefined
error because they cache.registered
is not added to the new cache.
Solution: I solved this problem by wrapping the @emotion/react provider ({my component}) in my component. You can try following the example provided by @emotion.react: https://emotion.sh/docs/cache-provider
Also make sure the dependencies from @emotion/react are installed correctly by running: npm install --save @emotion/react
or yarn add @emotion/react
The code should look like this:
This is dummyComponent.js using more familiar JSX syntax:
const React = require('react'); const { Box } = require('@mui/material'); const DummyComponent = (props) => { const { children } = props; const [showChild, setShowChild] = React.useState(false); return ( <Box> <label htmlFor="toggle">Show/Hide</label> <input id="toggle" type="checkbox" onChange={(e) => setShowChild(e.target.checked)} checked={showChild} /> {showChild && children} </Box> ); }; export default DummyComponent;
This is the mocha unit test:
const React = require('react'); const { expect } = require('chai'); const { render, fireEvent } = require('@testing-library/react'); const createCache = require("@emotion/cache"); const DummyComponent = require('./dummyComponent'); describe('pageCenter', function () { before(function () { this.jsdom = require('global-jsdom')(); }); after(function () { this.jsdom(); }); const myCache = createCache({ key: 'my-prefix-key' }); it('should render', function () { const { queryAllByText, getByLabelText } = render( <CacheProvider value={myCache}> <DummyComponent>Hi!</DummyComponent> </CacheProvider> ); expect(queryAllByText('Hi!')).should.have.length(0); fireEvent.click(getByLabelText('Show/Hide')); expect(queryAllByText('Hi!')).should.have.length(1); }); });
Note that I'm also using some JSX syntax in the test case and const { expect } = require('chai');
, which allows me to use should
and Chain call other functions from chai.