React - 在功能组件中测试外部功能

Posted

技术标签:

【中文标题】React - 在功能组件中测试外部功能【英文标题】:React - Testing external function in a functional component 【发布时间】:2019-08-06 22:23:40 【问题描述】:

我有一个使用反应钩子的功能组件。我有一个更新该组件状态的函数 (evaluateFunction)。

这个更新状态函数调用一个外部函数来检索数据,如下所示:

import  calculatePerformanceTime  from "../../helpers/calculate-performance-time";

const getChallenge = challengeNumber =>
    calculatePerformanceTime(
        require(`../../../challenges/$challengeNumber.js`)[
            `dcpChallenge$challengeNumber`
        ],
        challengeNumber
    );

export const TestComponent = _ => 
    const [inputs, setInputs] = useState();
    const [result, setResult] = useState();
    const evaluateFunction = value => setResult(getChallenge(value)(inputs));
    return (
        <div>
          <button onClick=() => evaluateFunction(1) />
        </div>
    );
;

当我模拟单击以测试是否已调用 calculatePerformanceTime 时,它会抛出此错误:

TypeError: getChallenge(...) is not a function

我尝试导出 getChallenge,但没有成功。

如何测试单击按钮时是否调用了该函数?

这是我迄今为止一直在测试的:

import React from "react";
import Adapter from "enzyme-adapter-react-16";
import  configure, shallow  from "enzyme";
import  ChallengeSolution  from "./ChallengeSolution";
import  calculatePerformanceTime  from "../../helpers/calculate-performance-time";
configure( adapter: new Adapter() );

const mockFunction = jest.fn();
const mockInputData = 1;

jest.mock(`!raw-loader!../../challenges/1.js`, () => "MOCK_RAW", 
    virtual: true
);

jest.mock(`!raw-loader!../../challenges/2.js`, () => "MOCK_RAW", 
    virtual: true
);

jest.mock("../../helpers/calculate-performance-time.js");

describe("ChallengeSolutionComponent", () => 
    let wrapper;
    const tabNumber = 2;
    beforeEach(() => 
        wrapper = shallow(<ChallengeSolution selectedTab=tabNumber />);
    );

    describe("when component was mount", () => 
        it("should render form correctly", () => 
            const title = wrapper.find(".challenge-solution__title");
            const button = wrapper.find(".challenge-solution__button");
            button.simulate("click");
            expect(calculatePerformanceTime).toHaveBeenCalled();
            expect(title.text()).toEqual(`Daily Coding Solution #$tabNumber`);
        );
    );
);

【问题讨论】:

你能分享calculatePerformanceTime的代码和你的测试吗? (您的代码期望 getChallenge 返回一个 function 但我看不到足够的代码来判断是什么导致您的测试期间中断) 我已经添加了我一直在做的测试。我认为这个问题是因为该组件是一个功能组件,并且由于getChallenge 在外面,它无法以某种方式到达它......如果你愿意,我可以分享calculatePerformanceTime(它很长)虽然getChallenge 是本身就是一个函数,所以calculatePerformanceTime的结果并不重要 你根本不需要测试它。代码不正确。钩子只能在顶层运行。相反,您创建了一个稍后将调用该钩子的新函数。这不是受支持的行为。 reactjs.org/docs/hooks-overview.html#rules-of-hooks。 UPD:或者我可能会被你提到的钩子误导。这个calculatePerformanceTime 函数是钩子还是常规函数? 不,calculatePerformanceTime 与钩子完全没有关系,只是一个返回某个值的函数。我确实在顶层使用了钩子,这个问题与模拟该特定功能有关。我需要将calculate-performance-time 作为全局模块导入才能正确模拟它:) 【参考方案1】:

这一行:

jest.mock("../../helpers/calculate-performance-time.js");

...将calculatePerformanceTime 设置为一个返回undefined 的空模拟函数。

由于getChallenge返回调用calculatePerformanceTime的结果,所以也返回undefined

那么,当这条线运行时:

const evaluateFunction = value => setResult(getChallenge(value)(inputs));

...它尝试将getChallenge(...) 的结果用作函数并使用inputs 调用它,但失败了,因为它试图将undefined 作为函数调用。


你需要模拟calculatePerformanceTime 来返回一个函数

import React from "react";
import Adapter from "enzyme-adapter-react-16";
import  configure, shallow  from "enzyme";
import  ChallengeSolution  from "./ChallengeSolution";
import * as calculatePerformanceTimeModule from "../../helpers/calculate-performance-time";  // import the module
configure( adapter: new Adapter() );

const mockFunction = jest.fn();
const mockInputData = 1;

jest.mock(`!raw-loader!../../challenges/1.js`, () => "MOCK_RAW", 
  virtual: true
);

jest.mock(`!raw-loader!../../challenges/2.js`, () => "MOCK_RAW", 
  virtual: true
);

const spy = jest.spyOn(calculatePerformanceTimeModule, 'calculatePerformanceTime');
spy.mockReturnValue(() =>  /* this returns a function...fill in the return value here */ );

describe("ChallengeSolutionComponent", () => 
  let wrapper;
  const tabNumber = 2;
  beforeEach(() => 
    wrapper = shallow(<ChallengeSolution selectedTab=tabNumber />);
  );

  describe("when component was mount", () => 
    it("should render form correctly", () => 
      const title = wrapper.find(".challenge-solution__title");
      const button = wrapper.find(".challenge-solution__button");
      button.simulate("click");
      expect(spy).toHaveBeenCalled();  // Success!
      expect(title.text()).toEqual(`Daily Coding Solution #$tabNumber`);
    );
  );
);

【讨论】:

以上是关于React - 在功能组件中测试外部功能的主要内容,如果未能解决你的问题,请参考以下文章

单元测试 React 功能组件的功能

React Native - 在功能组件中创建方法并在组件外部调用此方法,可能吗?

使用 Jest/Enzyme 在 React 功能组件中测试封闭组件

在 React 功能组件中使用 ref 添加测试鼠标事件监听器

使用 Jest / Enzyme 在 React 中的功能组件内部测试方法

如何使用 Jest/Enzyme 在功能性 React 组件中测试 lambda 函数?