使用 react-hooks-testing-library 进行测试时,自定义挂钩的当前结果会更新
The current result of a custom hook is updated when testing using react-hooks-testing-library
我正在测试如下所示的自定义 React Hook。它的作用是获取一张图片的尺寸,然后使用提供的props计算出用户想要的图片尺寸。
import { useEffect, useState } from 'react';
import { Image, LayoutAnimation, useWindowDimensions } from 'react-native';
export type SizeType =
| {
width: number;
height: number;
}
| undefined;
export const useImageSize = (imageUri: string, fillWidth?: boolean, fillHeight?: boolean, width?: number, height?: number): SizeType => {
const [size, setSize] = useState<SizeType>();
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
useEffect(() => {
Image.getSize(
imageUri,
(w, h) => {
LayoutAnimation.easeInEaseOut();
if (fillWidth) {
setSize({
width: screenWidth,
height: h * (screenWidth / w),
});
return;
}
if (fillHeight) {
setSize({
height: screenHeight,
width: w * (screenHeight / h),
});
return;
}
if (width && !height) {
setSize({
width,
height: h * (width / w),
});
return;
}
if (!width && height) {
setSize({
width: w * (height / h),
height: height,
});
}
},
() => {
setSize({ width: 0, height: 0 });
}
);
}, [imageUri, fillWidth, fillHeight, screenWidth, screenHeight, width, height]);
console.log('size', size);
return size;
};
这是这个钩子的测试。它模拟了 Image 组件的 getSize 方法,还模拟了 React Native 的 useWindowDimension 钩子。
import * as RN from 'react-native';
import { renderHook, act } from '@testing-library/react-hooks';
import { useImageSize } from '@app/hooks/useImageSize';
describe('useImageSize', () => {
const getSizeSpyOn = jest.spyOn(RN.Image, 'getSize').mockImplementation(jest.fn());
afterEach(() => {
jest.clearAllMocks();
});
it('should return the correct size when we want to fill the width of the screen', () => {
jest.spyOn(RN, 'useWindowDimensions').mockReturnValue({ width: 200, height: 200, fontScale: 1, scale: 1 });
const { result } = renderHook(() => {
useImageSize('test_image', true, false)
});
expect(getSizeSpyOn).toHaveBeenCalledTimes(1)
const getSizeArgs = getSizeSpyOn.mock.calls[0]!;
expect(getSizeArgs[0]).toBe('test_image');
const success = getSizeArgs[1];
void act(() => {
success(100, 50);
});
console.log('result.current', result.current)
expect(result.current).toEqual({
width: 200,
height: 100,
});
});
console.log('result.current', result.current)
expect(result.current).toEqual({
width: 200,
height: 100,
});
});
});
我运行测试后,hook中的console log由undefined变为{width: 200, height: 100 },这是预期值,但是,测试中的console log仍然是不明确的。我认为这种行为表明在测试期间,hook 的行为符合预期,只是由于某种原因,测试中 hook 的当前值在新值可用后没有更新。
有人知道怎么解决吗?
回调必须return钩子的结果,而不仅仅是调用它。更改此行:
const { result } = renderHook(() => {
useImageSize('test_image', true, false)
});
对此:
const { result } = renderHook(() =>
useImageSize('test_image', true, false)
);
请注意,我已删除回调中的 {...}
。
我正在测试如下所示的自定义 React Hook。它的作用是获取一张图片的尺寸,然后使用提供的props计算出用户想要的图片尺寸。
import { useEffect, useState } from 'react';
import { Image, LayoutAnimation, useWindowDimensions } from 'react-native';
export type SizeType =
| {
width: number;
height: number;
}
| undefined;
export const useImageSize = (imageUri: string, fillWidth?: boolean, fillHeight?: boolean, width?: number, height?: number): SizeType => {
const [size, setSize] = useState<SizeType>();
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
useEffect(() => {
Image.getSize(
imageUri,
(w, h) => {
LayoutAnimation.easeInEaseOut();
if (fillWidth) {
setSize({
width: screenWidth,
height: h * (screenWidth / w),
});
return;
}
if (fillHeight) {
setSize({
height: screenHeight,
width: w * (screenHeight / h),
});
return;
}
if (width && !height) {
setSize({
width,
height: h * (width / w),
});
return;
}
if (!width && height) {
setSize({
width: w * (height / h),
height: height,
});
}
},
() => {
setSize({ width: 0, height: 0 });
}
);
}, [imageUri, fillWidth, fillHeight, screenWidth, screenHeight, width, height]);
console.log('size', size);
return size;
};
这是这个钩子的测试。它模拟了 Image 组件的 getSize 方法,还模拟了 React Native 的 useWindowDimension 钩子。
import * as RN from 'react-native';
import { renderHook, act } from '@testing-library/react-hooks';
import { useImageSize } from '@app/hooks/useImageSize';
describe('useImageSize', () => {
const getSizeSpyOn = jest.spyOn(RN.Image, 'getSize').mockImplementation(jest.fn());
afterEach(() => {
jest.clearAllMocks();
});
it('should return the correct size when we want to fill the width of the screen', () => {
jest.spyOn(RN, 'useWindowDimensions').mockReturnValue({ width: 200, height: 200, fontScale: 1, scale: 1 });
const { result } = renderHook(() => {
useImageSize('test_image', true, false)
});
expect(getSizeSpyOn).toHaveBeenCalledTimes(1)
const getSizeArgs = getSizeSpyOn.mock.calls[0]!;
expect(getSizeArgs[0]).toBe('test_image');
const success = getSizeArgs[1];
void act(() => {
success(100, 50);
});
console.log('result.current', result.current)
expect(result.current).toEqual({
width: 200,
height: 100,
});
});
console.log('result.current', result.current)
expect(result.current).toEqual({
width: 200,
height: 100,
});
});
});
我运行测试后,hook中的console log由undefined变为{width: 200, height: 100 },这是预期值,但是,测试中的console log仍然是不明确的。我认为这种行为表明在测试期间,hook 的行为符合预期,只是由于某种原因,测试中 hook 的当前值在新值可用后没有更新。
有人知道怎么解决吗?
回调必须return钩子的结果,而不仅仅是调用它。更改此行:
const { result } = renderHook(() => {
useImageSize('test_image', true, false)
});
对此:
const { result } = renderHook(() =>
useImageSize('test_image', true, false)
);
请注意,我已删除回调中的 {...}
。