如何使用 Jest 和 react-testing-library 测试 react-dropzone?

Posted

技术标签:

【中文标题】如何使用 Jest 和 react-testing-library 测试 react-dropzone?【英文标题】:How to test react-dropzone with Jest and react-testing-library? 【发布时间】:2021-02-11 16:29:54 【问题描述】:

我想测试 React 组件中 react-dropzone 库中的 onDrop 方法。我正在使用 Jest,React 测试库。我正在创建模拟文件,并试图将这些文件放入输入中,但在 console.log 文件中仍然等于一个空数组。你有什么想法吗?

package.json

"typescript": "^3.9.7",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.0.4",
"@types/jest": "^26.0.13",
"jest": "^26.4.2",
"ts-jest": "^26.3.0",
"react-router-dom": "^5.1.2",
"react-dropzone": "^10.1.10",
"@types/react-dropzone": "4.2.0",

ModalImportFile.tsx

import React,  FC, useState  from "react";
import  Box, Button, Dialog, DialogContent, DialogTitle, Grid  from "@material-ui/core";
import  useDropzone  from "react-dropzone";
import AttachFileIcon from "@material-ui/icons/AttachFile";
import DeleteIcon from "@material-ui/icons/Delete";

interface Props 
    isOpen: boolean;


interface Events 
    onClose: () => void;


const ModalImportFile: FC<Props & Events> = props => 
    const  isOpen  = props as Props;
    const  onClose  = props as Events;

    const [files, setFiles] = useState<Array<File>>([]);

    const  getRootProps, getInputProps, open  = useDropzone(
        onDrop: (acceptedFiles: []) => 
            setFiles(
                acceptedFiles.map((file: File) =>
                    Object.assign(file, 
                        preview: URL.createObjectURL(file),
                    ),
                ),
            );
        ,
        noClick: true,
        noKeyboard: true,
    );

    const getDragZoneContent = () => 
        if (files && files.length > 0)
            return (
                <Box border=1 borderRadius=5 borderColor="#cecece" p=2 mb=2>
                    <Grid container alignItems="center" justify="space-between">
                        <Box color="text.primary">files[0].name</Box>
                        <Box ml=1 color="text.secondary">
                            <Button
                                startIcon=<DeleteIcon color="error" />
                                onClick=() => 
                                    setFiles([]);
                                
                            />
                        </Box>
                    </Grid>
                </Box>
            );
        return (
            <Box border=1 borderRadius=5 borderColor="#cecece" p=2 mb=2 style= borderStyle: "dashed" >
                <Grid container alignItems="center">
                    <Box mr=1 color="text.secondary">
                        <AttachFileIcon />
                    </Box>
                    <Box color="text.secondary">
                        <Box onClick=open component="span" marginLeft="5px">
                            Download
                        </Box>
                    </Box>
                </Grid>
            </Box>
        );
    ;

    const closeHandler = () => 
        onClose();
        setFiles([]);
    ;

    return (
        <Dialog open=isOpen onClose=closeHandler>
            <Box width=520>
                <DialogTitle>Import</DialogTitle>
                <DialogContent>
                    <div data-testid="container" className="container">
                        <div data-testid="dropzone" ...getRootProps( className: "dropzone" )>
                            <input data-testid="drop-input" ...getInputProps() />
                            getDragZoneContent()
                        </div>
                    </div>
                </DialogContent>
            </Box>
        </Dialog>
    );
;

export default ModalImportFile;

ModalImportFile.test.tsx

import React from "react";
import  render, screen, fireEvent  from "@testing-library/react";
import ModalImportFile from "../../components/task/elements/ModalImportFile";

const props = 
    isOpen: true,
    onClose: jest.fn(),
;

beforeEach(() => jest.clearAllMocks());

describe("<ModalImportFile/>", () => 
    it("should drop", async () => 
        render(<ModalImportFile ...props />);

        const file = new File([JSON.stringify( ping: true )], "ping.json",  type: "application/json" );
        const data = mockData([file]);

        function dispatchEvt(node: any, type: any, data: any) 
            const event = new Event(type,  bubbles: true );
            Object.assign(event, data);
            fireEvent(node, event);
        

        function mockData(files: Array<File>) 
            return 
                dataTransfer: 
                    files,
                    items: files.map(file => (
                        kind: "file",
                        type: file.type,
                        getAsFile: () => file,
                    )),
                    types: ["Files"],
                ,
            ;
        
        const inputEl = screen.getByTestId("drop-input");
        dispatchEvt(inputEl, "dragenter", data);
    );

【问题讨论】:

似乎 dropzone 方面提供的官方测试示例是如此糟糕和混乱,并且某些部分无法正常工作。您是否找到任何方法来测试包含此组件和 drop 事件的组件? @PouyaJabbarisani 我重写了测试组件,请看我的回答 【参考方案1】:

fireEvent(node, event); 更改为fireEvent.drop(node, event); 怎么样。

【讨论】:

欢迎来到 Stack Overflow。虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。 How to Answer 这对我没有帮助。文件仍然等于一个空数组 我将fireEvent(node, event); 更改为fireEvent.drop(node, event);,并在最后一行添加了await waitFor(() =&gt; expect(screen.getByText("ping.json")).toBeInTheDocument());,测试通过了。【参考方案2】:

根据 rokki 的回答 (https://***.com/a/64643985/9405587),我重写了测试组件以便于理解。

ModalImportFile.test.tsx

import React from "react";
import  render, screen, fireEvent  from "@testing-library/react";
import ModalImportFile from "../../components/task/elements/ModalImportFile";

const props = 
    isOpen: true,
    onClose: jest.fn(),
;

beforeEach(() => jest.clearAllMocks());

describe("<ModalImportFile/>", () => 
    it("should drop", async () => 
        render(<ModalImportFile ...props />);
        window.URL.createObjectURL = jest.fn().mockImplementation(() => "url");
        const inputEl = screen.getByTestId("drop-input");
        const file = new File(["file"], "ping.json", 
            type: "application/json",
        );
        Object.defineProperty(inputEl, "files", 
            value: [file],
        );
        fireEvent.drop(inputEl);
        expect(await screen.findByText("ping.json")).toBeInTheDocument();

【讨论】:

以上是关于如何使用 Jest 和 react-testing-library 测试 react-dropzone?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Jest 和 Proxyquire 存根模块?

如何在 React 和 Webpack 中使用 Jest

如何使用 jest.mock 模拟 useRef 和反应测试库

如何在 TypeScript 中使用 Jest 和 Knex 进行测试?

如何使用 NodeJS 解决 Supertest 和 jest 中的 ECONNREFUSED 错误?

如何使用 jest 和酶测试 onClick() 函数和 useState 钩子