首页 > 解决方案 > 如何使用 jest 对作为 prop 传递的函数进行单元测试

问题描述

我想在 Student 组件中测试 addTagclick

import React, { useState } from "react";

import "../stylesheets/Student.scss";
import AddTag from "./AddTag";
import Tag from "./Tag";

const Student = ({ student, addTagClick, tags }) => {
  tags = tags.filter((tag) => tag.studentId === student.id);
  const averageGrade =
    student.grades.reduce((acc, grade) => {
      return parseInt(acc) + parseInt(grade);
    }) / student.grades.length;
  const [isViewScores, setIsViewScores] = useState(false);

  const viewScoreClick = () => {
    setIsViewScores((prev) => !prev);
  };

  return (
    <article className="student">
      <figure>
        <img src={student.pic} alt="student" />
      </figure>
      <aside>
        <h2>
          {student.firstName} {student.lastName}
        </h2>
        <ul>
          <li>Email: {student.email}</li>
          <li>Company: {student.company}</li>
          <li>Skill: {student.skill}</li>
          <li>
            Average: {averageGrade}%
            <>
            {isViewScores && (
              <ul className="scores">
                {student.grades.map((grade, index) => {
                  return (
                    <li key={index}>
                      Test {index + 1}: {grade}%
                    </li>
                  );
                })}
              </ul>
            )}
            </>
          </li>
        </ul>
        <div className="tags-container">
          {tags.map((tag) => (
            <Tag tag={tag} key={tag.id} />
          ))}
        </div>
        <AddTag studentId={student.id} addTagClick={addTagClick} />
      </aside>
      <button onClick={viewScoreClick} className="view-scores-btn">
        {isViewScores ? "-" : "+"}
      </button>
    </article>
  );
};

export default Student;

AddTag 组件具有 addTagClick,它将将 newTag 作为参数传递给表单提交

import React, { useState } from "react";

import { generateId } from "../helper";
import Input from "./Input";

const AddTag = ({ studentId, addTagClick }) => {
  const [tag, setTag] = useState("");

  const handleInputChange = ({ target }) => {
    setTag(target.value);
  };

  const onSubmitClick = (e) => {
    e.preventDefault();

    const newTag = {
      tag: tag,
      id: generateId(),
      studentId: studentId,
    };

    if (tag) {
      addTagClick(newTag);
    }

    setTag("");
  };

  return (
    <form data-testid="form" onSubmit={onSubmitClick}>
      <Input
        className="add-tag-input"
        placeholder="Add a tag"
        type="text"
        value={tag}
        onChange={handleInputChange}
      />
      <button>add tag</button>
    </form>
  );
};

export default AddTag;

最后,在 StudentsContainer 组件中,将 newTag 添加到 tags 状态

import React, { useState, useMemo } from "react";
import Student from "./Student";
import Input from "./Input";

import "../stylesheets/StudentsContainer.scss";

const StudentsContainer = ({ students }) => {
  const [searchByName, setSearchByName] = useState("");
  const [searchByTags, setSearchByTags] = useState("");
  const [tags, setTags] = useState([]);

  const addTagClick = (newTag) => {
    setTags((prevTags) => [...prevTags, newTag]);
  };

  students = useMemo(
    () =>
      students.filter(
        ({ firstName, lastName, id }) =>
          searchByName.length < 2 ||
          (firstName + " " + lastName)
            .toLowerCase()
            .includes(searchByName.toLowerCase())
      ),
    [students, searchByName]
  );

  const renderStudentsBySearch = () => {
    if (searchByTags.length < 2) {
      return students;
    } else {
      return students.filter((student) => {
        for (let i = 0; i < tags.length; i++) {
          const tag = tags[i];
          if (tag.studentId === student.id && tag.tag.includes(searchByTags)) {
            return student;
          }
        }
      });
    }
  };

  return (
    <section className="students-container">
      <Input
        value={searchByName}
        placeholder="Search by name"
        onChange={({ target }) => setSearchByName(target.value)}
      />
      <Input
        className="tag-input"
        value={searchByTags}
        placeholder="Search by tag"
        onChange={({ target }) => setSearchByTags(target.value)}
      />
      <div className="students">
        {renderStudentsBySearch().map((student) => (
          <Student
            tags={tags}
            addTagClick={addTagClick}
            key={student.id}
            student={student}
          />
        ))}
      </div>
    </section>
  );
};

export default StudentsContainer;

这就是我到目前为止所拥有的,并且测试失败说调用次数:0

import React from "react";
import { render, fireEvent } from "@testing-library/react";

import Student from "../Student";
import { generateId } from "../../helper";

const mockStudent = {
  firstName: "Yoda",
  lastName: "Li",
  email: "yoda.li@gmail.com",
  city: "SF",
  company: "Dog",
  pic: "https://dog.com",
  skill: "eat",
  id: "8",
  grades: ["100", "88", "99", "89", "98", "100", "88", "98"],
};

const mockTags = [
  // { tag: "tag1", id: "8" },
  // { tag: "tag1", id: "7" },
];

describe("Student", () => {
  
  test("addTagClick is called with button click or with enter key, and newTag is added", () => {
    const addTagClickMock = jest.fn();
    const { getByPlaceholderText, getByText, getByTestId } = render(
      <Student
        student={mockStudent}
        tags={mockTags}
        addTagClick={addTagClickMock}
      />
    );

    const tagInput = getByPlaceholderText("Add a tag");
    const addTagButton = getByText("add tag");
    tagInput.value = "tag1";
    fireEvent.click(addTagButton);

    expect(addTagClickMock).toHaveBeenCalled();
    expect(addTagClickMock).toHaveBeenCalledWith({
      tag: tagInput.value,
      id: generateId(),
      studentId: mockStudent.id,
    });
  });
});

谢谢你的帮助!

标签: reactjsunit-testing

解决方案


推荐阅读