@jonrsharpe's suggestion is the general principle of component testing, but your question is a special case, involving some properties and methods of the DOM, such as offsetWidth and getBoundingClientRect() methods. The runtime environment of jsdom cannot return and the rendering engine under the browser runtime environment The returned result causes the offsetWidth and the property values returned by the getBoundingClientRect() method to always be 0.
So here we need mock ref.
The mock ref here is also special. In addition to using jest.mock() for partial mocking, the ref.current property will be assigned by react after the component is mounted. In order to intercept this operation, we need to create a mock ref object with a current object property, use the Object.defineProperty() method to define getter and setter for this ref.current property, and intercept property assignment.
jest.spyOn method takes an optional third argument of accessType that can be either 'get' or 'set', which proves to be useful when you want to spy on a getter or a setter, respectively.
E.g.
index.tsx:
import React, { useEffect, useRef } from 'react';
export default function App() {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
console.log(ref.current?.offsetWidth);
}, [ref]);
return <div ref={ref}>app</div>;
}
index.test.tsx:
import { render } from '@testing-library/react';
import React, { useRef } from 'react';
import { mocked } from 'ts-jest/utils';
import App from './';
jest.mock('react', () => {
return {
...jest.requireActual<typeof React>('react'),
useRef: jest.fn(),
};
});
const useMockRef = mocked(useRef);
describe('66332902', () => {
afterEach(() => {
jest.clearAllMocks();
});
afterAll(() => {
jest.resetAllMocks();
});
test('should mock ref and offsetWidth', () => {
const ref = { current: {} };
Object.defineProperty(ref, 'current', {
set(_current) {
if (_current) {
jest.spyOn(_current, 'offsetWidth', 'get').mockReturnValueOnce(100);
}
this._current = _current;
},
get() {
return this._current;
},
});
useMockRef.mockReturnValueOnce(ref);
render(<App />);
});
});
test result: (see the logs)
PASS examples/66332902/index.test.tsx (9.518 s)
66332902
✓ should mock ref and offsetWidth (39 ms)
console.log
100
at examples/66332902/index.tsx:6:13
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.106 s