如何基于文件读取器发出的事件测试反应组件



我有一个名为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,
}

最新更新