react-testing-library - 如何模拟文件上传到 <input type="file" /> 元素?

inb*_*33n 13 react-testing-library

我正在使用用户事件来尝试进行更“真实”的用户交互。但是,在我单击输入后,它不会触发该onChange功能,因为默认情况下它只会为用户打开文件浏览器以上传文件。如何模拟用户上传文件?

我的代码:

// Component
const FileInputComponent = ({ handleFileUpload }) => (
  <div>
    <input type="file" id="testing" accept=".png,.jpg" onChange={handleFileUpload} />
    <label htmlFor="testing">Input File Here</label>
  </div>
);
Run Code Online (Sandbox Code Playgroud)
// Test file
test("Clicking on the label button calls the `handleFileUpload` function", () => {
  const handleFileUploadMockFn = jest.fn();
  const { getByLabelText } = render(<FileInputComponent handleFileUpload={handleFileUploadMockFn} />
  userEvent.click(getByLabelText("Input File Here"));
  expect(handleFileUploadMockFn).toHaveBeenCalledTimes(1);
});
Run Code Online (Sandbox Code Playgroud)

Ili*_*Ili 32

我发现了一个有点 hacky 的解决方案,我不确定它是否符合测试中的最佳实践,但我会与您分享它可能会有所帮助

\n
describe("Upload files", () => {\n  let file;\n\n  beforeEach(() => {\n    file = new File(["(\xe2\x8c\x90\xe2\x96\xa1_\xe2\x96\xa1)"], "chucknorris.png", { type: "image/png" });\n  });\n\n  test("cover photo upload", async () => {\n    // render the component\n    const { getByTestId } = render(<YourComponent />);\n\n    // get the upload button\n    let uploader = getByTestId("photo-uploader");\n\n    // simulate ulpoad event and wait until finish\n    await waitFor(() =>\n      fireEvent.change(uploader, {\n        target: { files: [file] },\n      })\n    );\n    // get the same uploader from the dom\n    let image = document.getElementById("photo-uploader");\n    // check if the file is there\n    expect(image.files[0].name).toBe("chucknorris.png");\n    expect(image.files.length).toBe(1);\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n


小智 29

是您要测试的上传吗? https://github.com/testing-library/user-event

我只是写

const fileInput = getByLabelText('file-input-label')
userEvent.upload(fileInput, testFile)
Run Code Online (Sandbox Code Playgroud)

onChange为我模拟了。


小智 11

@darthzeren 的答案几乎是正确的,但该链接似乎已过时。以下是 lib 维护者描述的解决方案: https://testing-library.com/docs/ecosystem-user-event/#uploadelement-file--clickinit-changeinit--options

只是引用文档中的示例,以防链接将来不起作用:

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('upload file', () => {
  const file = new File(['hello'], 'hello.png', {type: 'image/png'})

  render(
    <div>
      <label htmlFor="file-uploader">Upload file:</label>
      <input id="file-uploader" type="file" />
    </div>,
  )
  const input = screen.getByLabelText(/upload file/i)
  userEvent.upload(input, file)

  expect(input.files[0]).toStrictEqual(file)
  expect(input.files.item(0)).toStrictEqual(file)
  expect(input.files).toHaveLength(1)
})

test('upload multiple files', () => {
  const files = [
    new File(['hello'], 'hello.png', {type: 'image/png'}),
    new File(['there'], 'there.png', {type: 'image/png'}),
  ]

  render(
    <div>
      <label htmlFor="file-uploader">Upload file:</label>
      <input id="file-uploader" type="file" multiple />
    </div>,
  )
  const input = screen.getByLabelText(/upload file/i)
  userEvent.upload(input, files)

  expect(input.files).toHaveLength(2)
  expect(input.files[0]).toStrictEqual(files[0])
  expect(input.files[1]).toStrictEqual(files[1])
})
Run Code Online (Sandbox Code Playgroud)


Flo*_*Flo 5

这是我们使用 RTL 测试文件输入的方法:

\n
    describe(\'input file\', () => {\n      let rtl: RenderResult;\n\n      beforeEach(() => {\n        rtl = render(<MyComponentWithFileInput />);\n      });\n\n      test(\'input file\', async () => {\n        const file = new File([\'(\xe2\x8c\x90\xe2\x96\xa1_\xe2\x96\xa1)\'], \'file.xml\', { type: \'application/xml\' });\n\n        // upload the file by updating the value attribute of the input\n        // I assume : <input type="file" data-testid="fileInput" />\n        fireEvent.change(rtl.getByTestId(\'fileInput\'), {\n          target: { files: [file] },\n        });\n\n        // here your onChange function should have been called\n        expect(handleFileUploadMockFn).toHaveBeenCalledTimes(1);\n\n        // if you have a form which is submitted you should be able to check values :\n        expect(onSubmitForm).toHaveBeenCalledWith({\n          file: expect.any(Object),\n        });\n      });\n    });\n
Run Code Online (Sandbox Code Playgroud)\n

这是总体思路,显然应该更新以匹配您的设置。

\n


Ina*_*man 5

输入元素:

<input type="file" data-testid="fileDropzone"/>
Run Code Online (Sandbox Code Playgroud)

首先渲染元素,然后通过 获取输入文件上传元素getByTestId

其次,定义一个假文件:

const fakeFile = new File(['hello'], 'hello.png', { type: 'image/png' });
Run Code Online (Sandbox Code Playgroud)

然后,导入 userEvent。并上传文件,确保在里面说唱act()

import userEvent from '@testing-library/user-event';
Run Code Online (Sandbox Code Playgroud)

测试期望,这是最终的代码:

it('Upload Files', async () => {
  const { getByTestId } = render(<FileUplaodComponent/>);
  const fakeFile = new File(['hello'], 'hello.png', { type: 'image/png' });
  const inputFile = getByTestId(/fileDropzone/i);
  await act(async () => {
    await waitFor(() => {
      userEvent.upload(inputFile, fakeFile);
    });
  });

  expect(inputFile.files[0]).toStrictEqual(inputFile);
});
Run Code Online (Sandbox Code Playgroud)