Невозможно прочитать свойство target of undefined при тестировании useState - PullRequest
0 голосов
/ 26 мая 2020

Я пытаюсь использовать эти методы состояния при передаче состояния из родительского компонента в дочерний компонент

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

, но я получаю эту ошибку

✓ Должен проверить, отображает ли EditProfile дочерний компонент (2 мс) ✕ должен проверять биологическое состояние (4 мс)

● Должен визуализировать ›должен проверять биологическое состояние

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;

1 Ответ

1 голос
/ 27 мая 2020

Неправильно то, как вы проверяете, что вызывается handleBio. Предполагается, что тесты имитируют c взаимодействия с пользователем, но здесь вы программно вызываете handleBio, что просто бесполезно. Вместо этого вы должны представить, что пользователь может делать с вашими компонентами и какие действия необходимо выполнить в таком случае. Здесь вы, похоже, проверяете, что setBio правильно вызывается при вызове handleBio, что просто проверяет, правильно ли React связывает реквизиты (и вам не нужно / не следует этого делать). Вместо этого вам нужно проверить, что handleBio был вызван при TextField change, и это должно произойти в EditProfileForm test.

Для этого вам нужно запустить событие изменения на TextField ( что произойдет, если пользователь изменит значение TextField), и только после этого ожидайте, что handleBio был вызван с правильными параметрами.

Я предлагаю вам использовать @ testing-library / response , что значительно упростит написание ваших тестов.

Вы можете добавить атрибут 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);
  }
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...