使用酶和玩笑时如何在测试中设置状态?

How to set state in test when using enzyme and jest?

首先,我有条件地呈现一个组件,该组件依赖于因 useEffect 挂钩而设置的 useState 挂钩。这是一个代码示例:

function Component() {
  const [response, setResponse] = useState();
  const [fail, setFail] = useState();
  /**
   * @function getModels - fetch data to populate the models
   * */
  const fetchStuff = async () => {
    fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then((data) => {
        const storage = [data];
        setResponse(storage);
        setFail(false);
      })
      .catch((err) => {
        setResponse(err);
        setFail(true);
      });
  };

  useEffect(() => {
    fetchStuff();
  }, []);

  if (fail === true) {
    return (
      <p>
        ERROR:
        {fail}
      </p>
    );
  }
  if (fail === false) {
    return (
      <p>
        Success:
        {response}
      </p>
    );
  }
  return <p>Loading Screen</p>;
}

我目前的争论点是我无法调用 setResponse 或 setFail 并更新失败或响应的状态。我相信我需要使用 mount 而不是浅渲染?另外,我知道测试哲学会反对以这种方式进行测试。但是,我正在寻找一种能够更新状态的解决方案。任何建议将不胜感激。

您可以模拟 fetch API 调用及其解析值。然后,您可以断言组件呈现的内容。我们应该使用 whenStable 函数来确保模拟的 API 调用已完成。

Component.jsx:

import React, { useState, useEffect } from 'react';

export function Component() {
  const [response, setResponse] = useState();
  const [fail, setFail] = useState();
  /**
   * @function getModels - fetch data to populate the models
   * */
  const fetchStuff = async () => {
    fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then((data) => {
        const storage = [data];
        setResponse(storage);
        setFail(false);
      })
      .catch((err) => {
        setResponse(err);
        setFail(true);
      });
  };

  useEffect(() => {
    fetchStuff();
  }, []);

  if (fail === true) {
    return (
      <p>
        ERROR:
        {fail}
      </p>
    );
  }
  if (fail === false) {
    return (
      <p>
        Success:
        {response}
      </p>
    );
  }
  return <p>Loading Screen</p>;
}

Component.test.jsx:

import React from 'react';
import { Component } from './Component';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';

const whenStable = async () => {
  await act(async () => {
    await new Promise((resolve) => setTimeout(resolve, 0));
  });
};

describe('65243384', () => {
  let fetch;
  beforeEach(() => {
    fetch = global.fetch;
  });
  afterEach(() => {
    global.fetch = fetch;
  });
  it('should success', async () => {
    global.fetch = jest.fn().mockResolvedValueOnce('mocked success data');
    const wrapper = mount(<Component></Component>);
    expect(wrapper.find('p').text()).toBe('Loading Screen');
    await whenStable();
    expect(wrapper.find('p').text()).toBe('Success:mocked success data');
    expect(global.fetch).toBeCalledWith('https://jsonplaceholder.typicode.com/todos/1');
  });

  it('should fail', async () => {
    const mErr = new Error('network');
    global.fetch = jest.fn().mockRejectedValueOnce(mErr);
    const wrapper = mount(<Component></Component>);
    expect(wrapper.find('p').text()).toBe('Loading Screen');
    await whenStable();
    expect(wrapper.find('p').text()).toBe('ERROR:');
    expect(global.fetch).toBeCalledWith('https://jsonplaceholder.typicode.com/todos/1');
  });
});

单元测试结果:

 PASS  examples/65243384/Component.test.jsx
  65243384
    ✓ should success (44 ms)
    ✓ should fail (5 ms)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |     100 |      100 |     100 |     100 |                   
 Component.jsx |     100 |      100 |     100 |     100 |                   
---------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        5.121 s

源代码:https://github.com/mrdulin/jest-v26-codelab/tree/main/examples/65243384