2 votes

Jest / Enzyme - fonction asynchrone fantaisie dans une méthode de cycle de vie

J'essaie de tester unitairement mon composant React avec Jest. Mes tests passent mais j'obtiens TypeError à cause de console.error() dans try/catch. Je pense que mon mock n'est pas configuré correctement mais j'ai essayé de le faire async sans aucun résultat. J'apprécie tout conseil.

erreur console.components/App.js:91 TypeError : Impossible de lire la propriété 'fetchPoints' d'undefined at App.fetchPoints ( D:\\components\App.js :87:48) à tryCatch ( D:\\node_modules\regenerator -runtime \runtime.js :62:40) at Generator.invoke [as _invoke] ( D:\\node_modules\regenerator -runtime \runtime.js :296:22) at Generator.prototype.(anonymous function) [as next] ( D:\\node_modules\regenerator -runtime \runtime.js :114:21) à l'étape ( D:\\components\App.js :38:191) sur D:\\components\App.js :38:437 à nouvelle Promesse () à App. ( D:\\components\App.js :38:99) at App.componentDidMount ( D:\\components\App.js :155:30) sur D:\\node_modules\react -test-renderer \lib\ReactCompositeComponent.js :262:25 à measureLifeCyclePerf ( D:\\node_modules\react -test-renderer \lib\ReactCompositeComponent.js :73:12) sur D:\\node_modules\react -test-renderer \lib\ReactCompositeComponent.js :261:11 at CallbackQueue.notifyAll ( D:\\node_modules\react -test-renderer \lib\CallbackQueue.js :74:22) at ReactTestReconcileTransaction.close ( D:\\node_modules\react -test-renderer \lib\ReactTestReconcileTransaction.js :34:26) at ReactTestReconcileTransaction.closeAll ( D:\\node_modules\react -test-renderer \lib\Transaction.js :207:25) at ReactTestReconcileTransaction.perform ( D:\\node_modules\react -test-renderer \lib\Transaction.js :154:16) at batchedMountComponentIntoNode ( D:\\node_modules\react -test-renderer \lib\ReactTestMount.js :67:27) at ReactDefaultBatchingStrategyTransaction.perform ( D:\\node_modules\react -test-renderer \lib\Transaction.js :141:20) at Object.batchedUpdates ( D:\\node_modules\react -test-renderer \lib\ReactDefaultBatchingStrategy.js :60:26) at Object.batchedUpdates ( D:\\node_modules\react -test-renderer \lib\ReactUpdates.js :95:27) at Object.render ( D:\\node_modules\react -test-renderer \lib\ReactTestMount.js :126:18) at Object.create ( D:\\components__tests__\App.test.js :26:31) at Object.asyncJestTest ( D:\\node_modules\jest -jasmine2 \build\jasmine_async.js :108:37) au moment de la résolution ( D:\\node_modules\jest -jasmine2 \build\queue_runner.js :56:12) at new Promise () at mapper ( D:\\node_modules\jest -jasmine2 \build\queue_runner.js :43:19) at promise.then ( D:\\node_modules\jest -jasmine2 \build\queue_runner.js :87:41) à l'adresse at process._tickCallback (internal/process/next_tick.js:188:7)

App.test.js

J'ai essayé d'utiliser mount() et fetchPoints pour renvoyer Promise.resolve().

import React from 'react';
    import renderer from 'react-test-renderer';
    import { shallow, configure, mount } from 'enzyme';
    import Adapter from 'enzyme-adapter-react-15';
    import { App } from '../App';

    configure({ adapter: new Adapter() });

    describe('App', () => {
        let wrapper;
        const apiMockActions = {
            fetchPoints : jest.fn()
        };

        beforeEach(() => {
            wrapper = shallow(<App actions={apiMockActions} />);
        });

        it('should call fetchPoints in #componentDidMount', () => {
            return wrapper.instance().componentDidMount().then(() => {
                expect(apiMockActions.fetchPoints).toHaveBeenCalled();
            });
        });

    });

App.js

import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import * as api from '../actions/api';
    import { bindActionCreators } from 'redux';

    export class App extends Component {
        constructor(props){
            super(props);
            this.state = {
                data: []
            }  
        }
        async componentDidMount() {
            try {
                let res = await this.props.actions.fetchPoints();
                //this.setState({ data: res });

            } catch (error) {
                console.error(error);
            }
        }

        render() {
            return (
                <div className="col-md-12 app__div">      
                </div>
            );
        }
    }

    function mapStateToProps(state) {
        return {
            data: state.points
        };
    }

    function mapDispatchToProps(dispatch) {
        return {
            actions: bindActionCreators(Object.assign({}, api), dispatch)
        };
    }

    export default connect(mapStateToProps, mapDispatchToProps)(App);

paquet.json

  "dependencies": {
        "ag-grid": "^14.2.0",
        "ag-grid-react": "^14.2.0",
        "axios": "^0.16.2",
        "babel-eslint": "^8.2.6",
        "babel-polyfill": "^6.26.0",
        "chart.js": "^2.7.1",
        "classnames": "^2.2.5",
        "eslint": "^5.2.0",
        "eslint-plugin-import": "^2.13.0",
        "eslint-plugin-react": "^7.10.0",
        "lodash": "^4.15.0",
        "moment": "^2.19.3",
        "prop-types": "^15.6.0",
        "rc-time-picker": "^2.4.1",
        "react": "^15.6.2",
        "react-addons-css-transition-group": "^15.6.2",
        "react-autosuggest": "^9.3.2",
        "react-bootstrap": "^0.31.5",
        "react-color": "^2.14.0",
        "react-datalist": "^4.0.0",
        "react-datepicker": "^0.29.0",
        "react-dom": "^15.6.2",
        "react-dom-factories": "^1.0.2",
        "react-dropzone": "^3.13.4",
        "react-grid-layout": "^0.15.3",
        "react-intl": "^2.4.0",
        "react-notification-system": "^0.2.16",
        "react-notify": "^2.0.1",
        "react-redux": "^5.0.6",
        "react-timepicker": "^1.3.1",
        "react-toggle-display": "^2.2.0",
        "redux": "^3.7.2",
        "redux-logger": "^3.0.6",
        "redux-promise-middleware": "^4.4.2",
        "redux-thunk": "^2.2.0",
        "section-iterator": "^2.0.0",
        "style-loader": "^0.13.2"
      },
      "devDependencies": {
        "babel-core": "^6.26.0",
        "babel-jest": "^23.4.0",
        "babel-loader": "^7.1.2",
        "babel-plugin-lodash": "^3.3.2",
        "babel-plugin-syntax-object-rest-spread": "^6.13.0",
        "babel-plugin-transform-class-properties": "^6.24.1",
        "babel-plugin-transform-decorators-legacy": "^1.3.4",
        "babel-preset-env": "^1.6.1",
        "babel-preset-es2015": "^6.24.1",
        "babel-preset-react": "^6.24.1",
        "babel-preset-stage-0": "^6.24.1",
        "chalk": "^2.3.0",
        "cross-env": "^5.1.1",
        "css-loader": "^0.23.1",
        "enzyme": "^3.3.0",
        "enzyme-adapter-react-15": "^1.0.6",
        "jest": "^23.4.1",
        "jest-cli": "^23.4.1",
        "parallel-webpack": "^1.5.0",
        "progress-bar-webpack-plugin": "^1.11.0",
        "react-test-renderer": "^15.6.2",
        "redux-immutable-state-invariant": "^2.1.0",
        "style-loader": "^0.13.1",
        "webpack": "^4.16.1",
        "webpack-cli": "^3.0.8"
      },

3voto

estus Points 5252

Des spies et des stubs doivent être créés pour chaque test :

let apiMockActions;

beforeEach(() => {
  apiMockActions = {
    fetchPoints : jest.fn()
  };
});

Les cycles de vie sont activés par défaut dans Enzyme 3 et les versions ultérieures, de sorte qu'appeler componentDidMount entraîne manuellement un double appel. Ils peut être désactivé afin d'enchaîner une promesse de componentDidMount :

beforeEach(() => {
    wrapper = shallow(<App actions={apiMockActions} />, { disableLifecycleMethods: true });
});

La valeur de la promesse retournée peut être simulée avec mockResolvedValue :

it('should call fetchPoints in #componentDidMount', async () => {
    const points = [...];
    const instance = wrapper.instance();
    apiMockActions.fetchPoints.mockResolvedValue(points);
    await instance.componentDidMount();
    expect(apiMockActions.fetchPoints).toHaveBeenCalled();
    expect(instance.state.data).toEqual({ data: points });
});

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X