如何在玩笑中模拟/间谍useState钩子?

时间:2020-10-02 01:26:13

标签: javascript reactjs testing enzyme jes

我试图监视useState React钩子,但我总是使测试失败

这是我的React组件:

const Counter= () => {
    const[counter, setCounter] = useState(0);

    const handleClick=() => {
        setCounter(counter + 1);
    }

    return (
        <div>
            <h2>{counter}</h2>
            <button onClick={handleClick} id="button">increment</button>
        </div>
    )
}

counter.test.js

it('increment counter correctlry', () => {
    let wrapper = shallow(<Counter/>);
    const setState = jest.fn();
    const useStateSpy = jest.spyOn(React, 'useState');

    useStateSpy.mockImplementation((init) => [init, setState]);
     const button = wrapper.find("button")
     button.simulate('click');
     expect(setState).toHaveBeenCalledWith(1);
})

不幸的是,这不起作用,并且我收到该消息的测试失败:

expected 1
Number of calls: 0

4 个答案:

答案 0 :(得分:5)

您需要使用The operator '>' isn't defined for the type 'DateTime'. 而不是单个导入var EventSource = require("eventsource"); var url = "..." // Source URL var es = new EventSource(url); es.onmessage = (event) => { console.log(event) const parsedData = JSON.parse(event.data); console.log(parsedData) }

我认为代码transpiled是如何获取的,正如您在babel repl中所看到的,一次导入的React.useState与模块导入的最终结果不同

useState

因此,您监视useState,但组件使用_react.useState // useState _react.default.useState // React.useState; 。 监视一次导入似乎是不可能的,因为您需要该函数属于一个对象,因此,这是一本非常详尽的指南,其中介绍了模拟/监视模块https://github.com/HugoDF/mock-spy-module-import

的方法。

正如@Alex Mackay所提到的,您可能希望改变对测试反应组件的看法,建议您改用react-testing-library,但是如果您确实需要坚持使用酶,则无需走太远嘲笑反应库本身

答案 1 :(得分:3)

令人讨厌的是,Codesandbox当前在其测试模块上遇到了麻烦,因此我无法发布一个有效的示例,但我将尝试解释为什么嘲笑useState通常是一件不好的事情。

用户不在乎是否调用了useState,他们关心的是当我单击增量时,计数应该增加一个,因此这就是您要测试的内容。 / p>

// App
import React, { useState } from "react";
export default function App() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
    </div>
  );
}
// Tests
import React from "react";
import App from "./App";
import { screen, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

describe("App should", () => {
  it('increment count value when "Increment" btn clicked', () => {
    // Render the App
    render(<App />);
    // Get the count in the same way the user would, by looking for 'Count'
    let count = screen.getByText(/count:/);
    // As long as the h1 element contains a '0' this test will pass
    expect(count).toContain(0);
    // Once again get the button in the same the user would, by the 'Increment'
    const button = screen.getByText(/increment/);
    // Simulate the click event
    userEvent.click(button);
    // Refetch the count
    count = screen.getByText(/count:/);
    // The 'Count' should no longer contain a '0'
    expect(count).not.toContain(0);
    // The 'Count' should contain a '1'
    expect(count).toContain(1);
  });
  // And so on...
  it('reset count value when "Reset" btn is clicked', () => {});
  it('decrement count value when "Decrement" btn is clicked', () => {});
});

如果您对此测试风格感兴趣,请绝对签出@testing-library。我从enzyme切换到大约2年前,此后一直没有碰过它。

答案 2 :(得分:3)

diedu 的回答为我指明了正确的方向,我想出了这个解决方案:

  1. 模拟使用 state 从 react 返回 jest.fn() 作为 useState: 1.1 之后立即导入 useState - 现在将是 e jest mock(从 jest.fn() 调用返回)

jest.mock('react', ()=>({
  ...jest.requireActual('react'),
  useState: jest.fn()
}))
import { useState } from 'react';

  1. 稍后在 beforeEach 中,将其设置为原始 useState,用于所有需要它不被模拟的情况

describe("Test", ()=>{
  beforeEach(()=>{
    useState.mockImplementation(jest.requireActual('react').useState);
    //other preperations
  })
  //tests
})

  1. 在测试中根据需要模拟它:

it("Actual test", ()=>{
  useState.mockImplementation(()=>["someMockedValue", someMockOrSpySetter])
})

离别笔记:虽然在“黑匣子”是单元测试中弄脏手在概念上可能有些错误,但有时这样做确实非常有用。

答案 3 :(得分:1)

只需要在测试文件中导入 React,例如:

import * as React from 'react';

之后就可以使用模拟功能了。

import * as React from 'react';

:
:
it('increment counter correctlry', () => {
    let wrapper = shallow(<Counter/>);
    const setState = jest.fn();
    const useStateSpy = jest.spyOn(React, 'useState');

    useStateSpy.mockImplementation((init) => [init, setState]);
     const button = wrapper.find("button")
     button.simulate('click');
     expect(setState).toHaveBeenCalledWith(1);
})