酶:模拟“点击”时,测试异步更改的状态值

时间:2019-03-08 17:39:26

标签: javascript reactjs jestjs enzyme create-react-app

我正在尝试测试一个state值(例如:state.name),该值在单击按钮时会异步更改。

最初

  • state.name"Random Value"
  • 单击 按钮 时,state.name"peter"变为"Random Value"
  • 在浏览器中按预期工作
  • 需要在我的 测试
  • 中在button.simulate('click')上使用相同的功能

我的组件App.js

import React, { Component } from 'react';

class App extends Component {
    state = {name: "Random Value"};
    render() {
        return (
            <div className="App">
                <div>{this.state.name}</div>
                <button onClick={this.handleClick}>GetData</button>
            </div>
        );
    }

    handleClick = () => {
        const currentContext = this;
        fetch('http://api-call/getdata')
            .then(function(response) {
                return response.json();
            })
            .then(function(jsonData) {
                // jsonData.name = "peter"
                currentContext.setState({name: jsonData.name});
            })
    }
}

export default App;

测试文件App.test.js

describe('<App/>', () => {    
    it('should handle click correctly', () => {
        const wrapper = shallow(<App />);

        expect(wrapper.find('button').length).toBe(1);

        expect(wrapper.state().name).toEqual("Random Value");
        wrapper.find('button').simulate('click');
        expect(wrapper.update().state().name).toEqual("peter");
    });
});

结果:

// FAILED!

Expected value to equal:
      "peter"
Received:
      "Random Value"

我还尝试了什么?

各种各样的解决方案,例如使用async awaitsetImmediate然后使用wrapper.update()等许多其他方式。也许我做错了什么或错过了什么。无论如何,我已经花了一个晚上试图做到这一点。我需要enzyme专家的帮助。

谢谢

1 个答案:

答案 0 :(得分:2)

首先,您需要以某种方式模拟fetch。发送真实的请求不仅破坏了单元测试的隔离,而且增加了不一致的风险。当您不知道响应何时完成时,也很难等待响应。有多种方法可以实现这一目标。 jest-fetch-mock是其中之一。

我还建议您不要检查state,而是检查render()的结果。

function getName(wrapper) {
    return wrapper.find('.App > div').at(0).props().children;
} 

it('should handle click correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({ name: '12345' }));
    const wrapper = shallow(<App />);

    expect(wrapper.find('button').length).toBe(1);

    expect(getName(wrapper)).toEqual("Random Value");
    wrapper.find('button').simulate('click');
    await Promise.resolve();
    expect(getName(wrapper)).toEqual("peter");
});

这是怎么回事。 await Promise.resolve()只是等待所有已解决的诺言得以执行。这意味着如果没有,我们的模拟响应将不会在按钮单击和expect运行之间运行。

获得正面结果的另一种方法是使handleClick()返回我们可以等待的承诺:

...
handleClick = () => {
    const currentContext = this;
    return fetch('http://api-call/getdata')
        .then(function(response) {
            return response.json();
        })
        .then(function(jsonData) {
            // jsonData.name = "peter"
            currentContext.setState({name: jsonData.name});
        })
}
....
it('should handle click correctly', async () => {
     ....
    expect(getName(wrapper)).toEqual("Random Value");
    await wrapper.find('button').simulate('click');
    expect(getName(wrapper)).toEqual("peter");
});

或没有异步等待语法:

it('should handle click correctly', () => {
     ....
    expect(getName(wrapper)).toEqual("Random Value");
    return wrapper.find('button').simulate('click').then(() => {
        expect(getName(wrapper)).toEqual("peter");
    });
});

但是我真的不喜欢这种方式,因为事件处理程序必须返回通常不会执行的Promise。

有关微任务(承诺)/宏任务(计时器,事件)的更多信息,请点击此处:https://abc.danch.me/microtasks-macrotasks-more-on-the-event-loop-881557d7af6f

更多关于测试异步代码的信息,最好检查他们的文档:https://jestjs.io/docs/en/asynchronous