我有一个名为FileReader的react组件,基于Dropzone react component(https://github.com/react-dropzone/react-dropzone(。这个文件阅读器在将文件放在 FileReader 的输入元素上时调用回调反应钩子,如下所示:
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { useDropzone } from 'react-dropzone';
function FilePicker(props) {
const { fileName, setFile, setFileName } = props;
const onDrop = useCallback((acceptedFiles) => {
const reader = new FileReader();
reader.onload = () => {
const binaryFile = reader.result;
setFile(binaryFile);
};
acceptedFiles.forEach((file) => {
setFileName(file.name);
return reader.readAsBinaryString(file);
});
}, [setFile, setFileName]);
const { getRootProps, getInputProps } = useDropzone({ onDrop });
return (
<div data-testid="file-picker" id="file-picker" {...getRootProps()}>
<div className="container-fluid inherit-height">
<input data-testid="file-picker-input" {...getInputProps()} />
<div className="row align-content-center inherit-height">
<p data-testid={fileName} className="text-center" id="file-picker-title">
{fileName !== '' ? fileName : 'Drag n drop a file here, or click to select file' }
</p>
</div>
</div>
</div>
);
}
FilePicker.propTypes = {
fileName: PropTypes.string.isRequired,
setFile: PropTypes.func.isRequired,
setFileName: PropTypes.func.isRequired,
};
export default FilePicker;
现在我正在尝试以一种可以检查函数 setFile 和 setFileName 是否已正确调用的方式测试此组件。我的测试文件如下所示:
import React from 'react';
import { render, cleanup, fireEvent, act } from '@testing-library/react';
import 'jest-dom/extend-expect';
import FileReader from './__mocks__/FileReader';
import FilePicker from './FilePicker';
afterEach(cleanup);
describe('React Use S3 Demo', () => {
it('should render file picker', () => {
const mockedFileName = '';
const mockedSetFile = jest.fn();
const mockedSetFileName = jest.fn();
const { rerender, getByTestId, debug } = render(
<FilePicker
fileName={mockedFileName}
setFile={mockedSetFile}
setFileName={mockedSetFileName}
/>,
);
const filePicker = getByTestId('file-picker-input');
act(() => {
fireEvent.drop(filePicker, {
target: {
files: [new File(['(⌐□_□)'], 'chucknorris.png', { type: 'image/png' })],
},
});
});
expect(mockedSetFileName).toHaveBeenCalled();
expect(mockedSetFile).toHaveBeenCalled();
});
});
乍一看,这似乎可以工作,但实际上并非如此,因为FileReader组件似乎没有调用作为道具的两个函数。我尝试使用"@testing库/反应"的重新渲染函数,但这似乎也没有帮助。您将如何测试 FileReader 以便在调用的 setFile 和 setFileName 函数上验证您的测试?
谢谢!
我找到了一种在测试中触发reader.onload
的方法。
我遇到的产品是,当我嘲笑FileReader
时,onload
处理程序从未被解雇。因此,我创建了一个mockFileReader
,该在调用readAsDataURL
时调用onload
处理程序。您可以轻松使用任何readAs
方法,具体取决于您在产品代码中使用的内容。
这是我的产品代码(简化(
import * as React from 'react'
import { uploadPhoto } from 'services/photos'
import { PhotoDropzone } from 'modules/PhotoDropzone'
export const UploadPhotoComponent: React.FC = () => {
const onDrop = React.useCallback((acceptedFiles: File[]) => {
const reader = new FileReader()
reader.onload = () => {
uploadPhoto({encodedPhoto: reader.result?.toString()})
}
reader.readAsDataURL(acceptedFiles[0]
}
return <PhotoDropzone aria-label='Upload photo' onDrop={onDrop} />
}
这是我的测试代码:
import * as photosService from 'services/photos'
import { successfulAxiosResponse } from 'mocks/axios'
import { mockFileReader } from 'mocks/fileReader'
import { screen, render, fireEvent } from '@testing-library/react'
it('upload gets called, when file dropped on dropzone', async () => {
const uploadProfilePhotoSpy = jest.spyOn(photosService, 'uploadPhoto')
.mockResolvedValue(successfulAxiosResponse)
const fileReaderConstructorSpy = jestspyOn(global, 'FileReader')
.mockReturnValue(mockFileReader)
const file = new File(['fake file contents'], 'profilePhoto.png', {
type: 'image/png',
})
setup() // this does the render
const uploadInput = screen.getByLabelText('Upload profile photo')
fireEvent.drop(uploadInput, {files: [file]})
await waitFor(() => expect(fileReaderConstructorSpy).toHaveBeenCalledTimes(1))
mockFileReader.onload = jest.fn()
await waitFor(() => expect(uploadProfilePhotoSpy).toHaveReturnedTimes(1))
}
这是我的模拟文件阅读器,以防有帮助:
const mockedReadAsDataURL = jest.fn().mockImplementation((args) =>
mockFileReader.onload && mockFileReader.onload(args))
export const mockFileReader: FileReader = {
abort: jest.fn(),
addEventListener: jest.fn(),
dispatchEvent: jest.fn(),
DONE: 0,
EMPTY: 0,
error: null,
LOADING: 0,
onabort: jest.fn(),
onerror: jest.fn(),
onload: jest.fn(),
onloadend: jest.fn(),
onloadstart: jest.fn(),
onprogress: jest.fn(),
readAsArrayBuffer: jest.fn(),
readAsBinaryString: jest.fn(),
readAsDataURL: mockedReadAsDataURL,
readAsText: jest.fn(),
readyState: 0,
removeEventListener: jest.fn(),
result: null,
}