Jest手动模拟仅适用于第一个函数调用

时间:2017-07-03 13:18:36

标签: javascript reactjs mocking jestjs

我使用Jest手动模拟功能嘲笑了我编写的自定义XHR包装器( utils / xhr.js ),我遇到的问题是仅跟踪第一个XHR呼叫

utils的/ xhr.js

let xhr = {
    get: function(params) { /* XHR get implementation */ }
    post: function(params) { /* XHR post implementation */ }
};

export default xhr;

utils / __ mocks __ / xhr.js - 模拟实现

let xhr = jest.genMockFromModule('./../xhr');

module.exports = xhr;

Dashboard.api.js - XHR调用仪表板组件

// ...

getTrendingEntities: (days, maxItems) => {

    return xhr.get({url: '/api/aggregator/riskEntities/' +  days + '/' + maxItems})
        .then((response) => {
            companyIds = parseCompanies(response.body);

            return xhr.post({
                url: '/api/entities/specific-companies',
                data: companyIds
            });

        }).then((response) => {
            let companies = parseCompaniesData(response.body);

            return Promise.resolve(companies);
        });
}

// ...

TrendingEntitiesPod.jsx - 使用Dashboard.api.js的React组件

class TrendingEntitiesPod extends React.Component {

    // ...

    componentWillMount() {
        this.loadData(this.props.days)
    }

    componentWillReceiveProps(nextProps) {
        if (this.props.days != nextProps.days) {
            this.loadData(nextProps.days);
        }
    }

    loadData(days) {
        this.setState({loading: true});

        api.getTrendingEntities(days, DASHBOARD_PODS_ENTRIES_NUMBER)
            .then((companies) => {
                this.setState({
                    companies: companies,
                    loading:   false
                });
            });
    }

    render() { // ... }

}

StartPage.TrendingEntitiesPod.spec.js - 测试文件

import React from 'react';
import { mount } from 'enzyme';

import xhr from './../../../utils/xhr';
jest.mock('./../../../utils/xhr');

import TrendingEntitiesPod from './../Dashboard/components/TrendingEntitiesPod/TrendingEntitiesPod.jsx';

describe('StartPage.TrendingEntitiesPod:', () => {

    let wrapper = null;

    beforeAll(() => {
        xhr.get.mockReturnValueOnce(Promise.resolve({body: trendingEntities}));
        xhr.post.mockReturnValueOnce(Promise.resolve({body: trendingEntitiesData}));
        xhr.get.mockReturnValue(Promise.resolve({body: trendingEntityPestleData}));

        wrapper = mount(<TrendingEntitiesPod days={30} />);

    });

    test('...stubbing works', () => {
        expect(xhr.get.mock.calls.length).toBe(1);
        expect(xhr.post.mock.calls.length).toBe(1); // returns false - why??
    });

});

2 个答案:

答案 0 :(得分:0)

似乎在Enzyme中安装了一个执行多个AJAX调用和多个状态更改的组件,在组件的第一个生命周期阶段,会使单元测试先运行,然后反映状态更改。

这是由于AJAX和setState()调用的异步性质。

我找到的修复是在 beforeAll 块中添加完成回调,该回调在 setTimeout(已完成,0)结束:

beforeAll((done) => {
    xhr.get.mockReturnValueOnce(Promise.resolve({body: trendingEntities}));
    xhr.post.mockReturnValueOnce(Promise.resolve({body: trendingEntitiesData}));
    xhr.get.mockReturnValue(Promise.resolve({body: trendingEntityPestleData}));

    wrapper = mount(<TrendingEntitiesPod days={30} />);

    // Delay the execution of tests, at the end, after the component
    // has been mounted, all XHR, setState and render calls are done.
    setTimeout(done, 0);
});

答案 1 :(得分:0)

另一种处理这个问题的方法,因为似乎Jest在执行setTimeout()时不能很好地发挥作用,就是在所有初始请求完成后不测试组件的状态。您可以测试执行XHR请求的各个方法(以及数据解析逻辑/组件状态更改)。 E.g:

AirlineCompanies.jsx

class AirlineCompanies extends React.Component {

    // ...

    loadData(numberOfCompanies) {
        this.setState({loading: true});

        return api.getAirlineCompanies(numberOfCompanies)
            .then((companies) => {
                this.setState({
                companies: companies,
                loading:   false
            });
        })
        .catch((error) => {
            utils.logError(error, 'AirlineCompanies.loadData():');
            this.setState({loading: false});
        });
    }

    // ...

}

AirlineCompanies.spec.js

let component = mount(<AirlineCompanies />).instance();
let airlineCompaniesData = [ /*... mock companies data ...*/ ];

it('...should have the state changed and eight AirlineCompany components after data is loaded', (done) => {
    xhr.get.mockReturnValueOnce(Promise.resolve({body: airlineCompaniesData}));

    component.loadData(30).then(() => {
        expect(wrapper.state('companies')).toEqual(airlineCompaniesData);
        expect(wrapper.find(AirlineCompany).length).toBe(8);
        done();
    });

});