测试useState时,无法读取undefined的'target'属性。

问题描述 投票:0回答:1

我正在尝试这些状态方法,当把状态从父组件传递到子组件时。

const [bio, setBio] = useState("");
const [gravatar, setGravatar] = useState("");

但我得到这个错误

✓应该检查EditProfile是否渲染子组件(2ms) ✕应该测试生物状态(4ms)。

应该呈现'应该测试生物状态

TypeError: Cannot read property 'target' of undefined

当这个测试被调用时,我做错了什么? 谢谢。

 it("should test bio state", () => {
        wrapper
            .find("EditProfileForm")
            .props()
            .handleBio();
        expect(setState).toHaveBeenCalledWith("set bio");
    });

editProfile.test.tsx

import React from "react";
import EditProfile from "./editProfile";
import { shallow, mount, render } from "enzyme";
import EditProfileForm from "../forms/editProfile/editForm";
import { createShallow } from "@material-ui/core/test-utils";
import { Provider } from "react-redux";
import { store } from "../../store";

describe("Should render <EditProfile/>", () => {
    let wrapper;
    const setState = jest.fn();
    const useStateSpy = jest.spyOn(React, "useState");
    useStateSpy.mockImplementation((init) => [init, setState]);

    beforeAll(() => {
        wrapper = mount(
            <Provider store={store}>
                <EditProfile />
            </Provider>,
        );
    });

    it("Should render <EditProfile/>", () => {
        expect(wrapper).toHaveLength(1);
    });

    it("Should check if EditProfile renders child component", () => {
        expect(wrapper.find("EditProfileForm")).toHaveLength(1);
    });

    it("should test bio state", () => {
        wrapper
            .find("EditProfileForm")
            .props()
            .handleBio();
        expect(setState).toHaveBeenCalledWith("set bio");
    });
});

editProfile.tsx

import React, { useEffect, Component, useRef, useState } from "react";
import Typography from "@material-ui/core/Typography";
import EditProfileForm from "../forms/editProfile/editForm";
import GridHoc from "../hoc/grid";
import { Grid } from "@material-ui/core";
import storeMethods from "../../common/storeHooks";

function EditProfile(props: any) {
    const [bio, setBio] = useState("");
    const [gravatar, setGravatar] = useState("");
    const mounted = useRef<Object>();
    const { getProfile, profileData, userErr, message, updateProfile } = storeMethods();
    useEffect(() => {
        if (!mounted.current) {
            getProfile();
            mounted.current = true;
        } else {
            setBio(bio ? bio : profileData.bio);
            setGravatar(gravatar ? gravatar : profileData.gravatar);
        }
    });
    const handleSubmit = (e: any) => {
        e.preventDefault();
        const formData = {
            bio,
            gravatar,
        };
        updateProfile(formData);
    };

    return (
        <Grid container={true} justify="center">
            <Grid item={true} xs={12} sm={12} md={8} lg={8}>
                {userErr && <Typography style={{ color: "red" }}>{message || userErr}</Typography>}
                {message && <Typography style={{ color: "green" }}>{message || userErr}</Typography>}
                <EditProfileForm handleBio={(e) => setBio(e.target.value)} handleGravatar={(e) => setGravatar(e.target.value)} onSubmit={handleSubmit} bio={bio} gravatar={gravatar} />
            </Grid>
        </Grid>
    );
}

export default GridHoc(EditProfile);

editProfileForm

import Button from "@material-ui/core/Button";
import FormGroup from "@material-ui/core/FormGroup";
import FormLabel from "@material-ui/core/FormLabel";
import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import React from "react";
const EditProfileForm = (props: any) => (
    <form onSubmit={props.onSubmit}>
        <Typography variant="h5">Edit Profile</Typography>
        <FormGroup style={{ padding: "30px 0px" }}>
            <FormLabel style={{ display: "block" }}>Bio</FormLabel>
            <TextField
                id="outlined-name"
                style={{
                    width: "100%",
                }}
                name="bio"
                multiline={true}
                rows="3"
                defaultValue={props.bio}
                onChange={props.handleBio}
                margin="normal"
                variant="outlined"
            />
            <FormLabel style={{ display: "block" }}>Gravatar</FormLabel>
            <TextField
                id="outlined-name"
                style={{
                    width: "100%",
                }}
                name="gravatar"
                multiline={true}
                rows="3"
                onChange={props.handleGravatar}
                defaultValue={props.gravatar}
                margin="normal"
                variant="outlined"
            />
        </FormGroup>
        <Button className="subBtn" variant="outlined" color="primary" type="submit">
            Submit
        </Button>
    </form>
);

export default EditProfileForm;
reactjs enzyme
1个回答
1
投票

你的测试方式有问题吗?handleBio 已被调用。测试应该是模仿用户的交互,但在这里,你以编程方式调用了 handleBio 相反,你应该想象用户可能会用你的组件做什么,以及哪些操作必须被执行。setBio 时被正确调用。handleBio 的调用,这只是测试React是否正确绑定了道具(你不需要不应该这样做)。handleBio 受访 TextField 变化,这应该发生在 EditProfileForm 测试。

要做到这一点,您需要在 TextField (如果用户修改了 TextField 值),只有在这之后,才会期待 handleBio 已被调用,且参数正确。

我建议你用 @testing-libraryreact 这将使你的测试更容易编写。

你可以添加一个 label 的属性。TextField :

<TextField name="bio" label="bio" />
<TextField name="gravatar" label="gravatar" />

然后写下下面的测试。

// existing imports
import { render as testRender, fireEvent, screen } from '@testing-library/react';

const props = {
  onSubmit: jest.fn(),
  handleBio: jest.fn(),
  handleGravatar: jest.fn(),
}

const render = () => testRender(<EditProfileForm {...props} />;

describe('EditProfile', () => {

  ...

  it('calls handleBio on bio TextField change', () => {
    render();
    const input = screen.getByLabelText('bio');

    fireEvent.change(input, { target: { value: 'new value' } });

    expect(props.handleBio).toHaveBeenCalledTimes(1);
  }
});
© www.soinside.com 2019 - 2024. All rights reserved.