【问题标题】:How to wait in enzyme for a promise from a private function?如何在酶中等待私人功能的承诺?
【发布时间】:2018-03-30 14:55:42
【问题描述】:

我是 react 和任何 javascript 测试框架的新手。

我有一个简单的组件,它从 API 中检索项目并将它们显示到屏幕上。

从componentWillMount调用函数getItems()。

是否可以等到 getItems() 完成后再进行断言?

ItemDetails.js

class ItemDetails extends Component {
    constructor(props) {
        super(props);
        this.state = {
            details: ''
        }
    }

    componentWillMount() {
        this.getItem();
    }

    getItem() {
        const itemId = this.props.match.params.id;
        fetch(`/api/items/${itemId}`)
            .then(res => res.json())
            .then(details => this.setState({ details }));
    }

    render() {
        const details = this.state.details;
        return (
            <div>
                <h1>{details.title}</h1>
                ...
            </div>
        );
    }
}

export default ItemDetails;

ItemDetails.test.js

describe('ItemDetails', () => {
    it('should render a div with title', () => {
        const details = {
            _id: 1,
            title: 'ItemName'
        };
        fetch.mockResponseOnce(JSON.stringify(details));
        const wrapper = mount(<ItemDetails match={{ params: {id: 1} }} />);
        expect(wrapper.find('div').find('h1').text()).toBe('ItemName');

    });
});

【问题讨论】:

    标签: reactjs fetch enzyme jestjs


    【解决方案1】:

    上面的答案有效,但它邀请测试实现细节:

    • 根据经验,访问包装器实例 (.instance()) 和调用 update() 是测试的味道,在我看来,它们对被测代码的工作方式了解太多,它不是在测试行为,它正在测试一个实现
    • 让方法返回一个 Promise 并在执行该期望之前等待该 Promise 破坏了方法的隐私:它使代码难以重构

    您真正想要的是等待所有承诺得到解决,而不访问这些承诺的句柄(这会破坏隐私)。试试这个

    const wait = () => new Promise(resolve => setTimeout(resolve));
    
    it('does something', () => {
      renderFnThatCallsAPromiseInternally();
    
      return wait().then(() => {
        expect(somethingDependentOnPromiseExecution).to.be.present();
      });
    });
    

    这将等待所有内部承诺解决,前提是 wait 在排队承诺(渲染组件)的代码被调用之后被调用。原因在于 JS 事件循环是如何工作的,它很挑剔,但 Jake Archibald 在这次演讲中很好地解释了:https://www.youtube.com/watch?v=cCOL7MC4Pl0

    希望对您有所帮助。

    【讨论】:

    • 这不是开玩笑的,试着在'expect'前加一个日志,你会发现日志不会被执行,结果还是对的。这是因为如果 jest 在函数中没有找到“期望”代码,那么 jest 会将其视为正确的。
    • 工作得很好,谢谢!我在断言之前使用了await wait(),而不是将它们包装在返回中。
    【解决方案2】:

    你可以试试:

    describe('ItemDetails', () => {
        it('should render a div with title', () => {
            const details = {
                _id: 1,
                title: 'ItemName'
            };
            fetch.mockResponseOnce(JSON.stringify(details));
            const wrapper = shallow(<ItemDetails match={{ params: {id: 1} }} />);      
    
            // manually call function
            wrapper.instance().getItem();
            // update to re-render component
            wrapper.update();
    
            expect(wrapper.find('div').find('h1').text()).toBe('ItemName');    
        });
    });
    

    如果它没有帮助,我认为你需要从你的函数中返回 Promise (base on this example):

    getItem() {
        const itemId = this.props.match.params.id;
        return fetch(`/api/items/${itemId}`)
            .then(res => res.json())
            .then(details => this.setState({ details }));
    }
    
    
    describe('ItemDetails', () => {
        it('should render a div with title', () => {
            const details = {
                _id: 1,
                title: 'ItemName'
            };
            fetch.mockResponse(JSON.stringify(details)); //response gets called twice
            const wrapper = mount(<ItemDetails match={{ params: {id: 1} }} />);
    
            // return Promise so Jest will wait until it's finished
            return wrapper.instance().getItem().then(() => {
              wrapper.update();
            }).then(() => {
             expect(wrapper.find('div').find('h1').text()).toBe('ItemName'); 
            })
        });
    });
    

    【讨论】:

    • 我不喜欢它两次调用 API,但它(第二次)有效。感谢您的快速响应。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-16
    • 2020-05-15
    • 2021-10-10
    • 2021-03-08
    相关资源
    最近更新 更多