I have a tsx
file which contains three react components:
import {FC} from 'react';
export const ComponentA: FC<{booleanProp: boolean}> = ({booleanProp}) => {
return (
<>
{booleanProp ? (
<ComponentB />
) : (
<ComponentC />
)}
</>
);
};
export const ComponentB: FC = () => {
return <span>ComponentB</span>;
};
export const ComponentC: FC = () => {
return <span>ComponentC</span>;
};
I want to test ComponentA
and mock ComponentB
and ComponentC
.
This is my test file:
import {FC} from 'react';
import {createRoot, Root} from 'react-dom/client';
import {act} from 'react-dom/test-utils';
import {ComponentA} from './my-components';
jest.mock('./my-components', () => {
const ComponentBMock: FC = () => {
return <span>ComponentB Mock</span>;
};
const ComponentCMock: FC = () => {
return <span>ComponentC Mock</span>;
};
return {
...jest.requireActual('./my-components'),
ComponentB: ComponentBMock,
ComponentC: ComponentCMock,
};
});
describe('ComponentA', () => {
let container: Element | null = null;
let root: Root | null = null;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
root = createRoot(container);
});
afterEach(() => {
act(() => {
root?.unmount();
root = null;
});
container?.remove();
container = null;
});
it('should render "ComponentB" when booleanProp is true', () => {
act(() => {
root?.render(<ComponentA booleanProp={true}/>);
});
expect(container?.textContent).toEqual('ComponentB Mock');
});
it('should render "ComponentC" when booleanProp is false', () => {
act(() => {
root?.render(<ComponentA booleanProp={false}/>);
});
expect(container?.textContent).toEqual('ComponentC Mock');
});
});
The problem is that the mocks doesn't seem to take effect and these are the tests result:
Expected: "ComponentB Mock"
Received: "ComponentB"
Expected: "ComponentC Mock"
Received: "ComponentC"
When I debugged the jest mock callback it appears to be called twice. In the first time the requireActual
returned undefined
for every component, and in the second time it has the real components values.
What am I missing? Thanks for your help!
CodePudding user response:
So after playing with it and read more about mocking this solution solved my problem.
The difference is the way I import my components and used jest.spyOn
to mock them:
import * as MyComponents from './my-components';
const ComponentA = MyComponents.ComponentA;
jest.spyOn(MyComponents, 'ComponentB').mockReturnValue(<span>ComponentB Mock</span>);
jest.spyOn(MyComponents, 'ComponentC').mockReturnValue(<span>ComponentC Mock</span>);
Of course that if you you need to remove the created mock you can restore the mock by calling the spy.mockRestore
function.
The full test file:
import {createRoot, Root} from 'react-dom/client';
import {act} from 'react-dom/test-utils';
import * as MyComponents from './my-components';
const ComponentA = MyComponents.ComponentA;
jest.spyOn(MyComponents, 'ComponentB').mockReturnValue(<span>ComponentB Mock</span>);
jest.spyOn(MyComponents, 'ComponentC').mockReturnValue(<span>ComponentC Mock</span>);
describe('ComponentA', () => {
let container: Element | null = null;
let root: Root | null = null;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
root = createRoot(container);
});
afterEach(() => {
act(() => {
root?.unmount();
root = null;
});
container?.remove();
container = null;
});
it('should render "ComponentB" when booleanProp is true', () => {
act(() => {
root?.render(<ComponentA booleanProp={true}/>);
});
expect(container?.textContent).toEqual('ComponentB Mock');
});
it('should render "ComponentC" when booleanProp is false', () => {
act(() => {
root?.render(<ComponentA booleanProp={false}/>);
});
expect(container?.textContent).toEqual('ComponentC Mock');
});
});