web-dev-qa-db-fra.com

Comment tester les composants connectés React-Redux?

J'utilise Mocha, Chai, Karma, Sinon, Webpack pour les tests unitaires.

J'ai suivi ce lien pour configurer mon environnement de test pour React-Redux Code.

Comment implémenter le test + la couverture du code sur React avec Karma, Babel et Webpack

Je peux tester avec succès mon code javascript action et réducteurs, mais quand il s'agit de tester mes composants, il génère toujours une erreur.

import React from 'react';
import TestUtils from 'react/lib/ReactTestUtils'; //I like using the Test Utils, but you can just use the DOM API instead.
import chai from 'chai';
// import sinon from 'sinon';
import spies from 'chai-spies';

chai.use(spies);

let should = chai.should()
  , expect = chai.expect;

import { PhoneVerification } from '../PhoneVerification';

let fakeStore = {
      'isFetching': false,
      'usernameSettings': {
        'errors': {},
        'username': 'sahil',
        'isEditable': false
      },
      'emailSettings': {
        'email': '[email protected]',
        'isEmailVerified': false,
        'isEditable': false
      },
      'passwordSettings': {
        'errors': {},
        'password': 'showsomestarz',
        'isEditable': false
      },
      'phoneSettings': {
        'isEditable': false,
        'errors': {},
        'otp': null,
        'isOTPSent': false,
        'isOTPReSent': false,
        'isShowMissedCallNumber': false,
        'isShowMissedCallVerificationLink': false,
        'missedCallNumber': null,
        'timeLeftToVerify': null,
        '_verifiedNumber': null,
        'timers': [],
        'phone': '',
        'isPhoneVerified': false
      }
}

function setup () {
    console.log(PhoneVerification);
    // PhoneVerification.componentDidMount = chai.spy();
    let output = TestUtils.renderIntoDocument(<PhoneVerification {...fakeStore}/>);
    return {
        output
    }
}

describe('PhoneVerificationComponent', () => {
    it('should render properly', (done) => {
        const { output } = setup();
        expect(PhoneVerification.prototype.componentDidMount).to.have.been.called;
        done();
    })
});

L'erreur suivante apparaît avec le code ci-dessus.

FAILED TESTS:
  PhoneVerificationComponent
    ✖ should render properly
      Chrome 48.0.2564 (Mac OS X 10.11.3)
    Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.

J'ai essayé de passer d'espions sinon à des espions chai.

Comment dois-je tester les composants connectés React-Redux (composants intelligents)?

47
Ayushya

Une meilleure façon de procéder consiste à exporter à la fois votre composant brut et le composant encapsulé dans connect. L'exportation nommée serait le composant, la valeur par défaut est le composant encapsulé:

export class Sample extends Component {

    render() {
        let { verification } = this.props;
        return (
            <h3>This is my awesome component.</h3>
        );
    }

}

const select = (state) => {
    return {
        verification: state.verification
    }
}

export default connect(select)(Sample);

De cette façon, vous pouvez importer normalement dans votre application, mais en ce qui concerne les tests, vous pouvez importer votre exportation nommée à l'aide de import { Sample } from 'component'.

46
Ashwin van Dijk

Vous pouvez tester votre composant connecté et je pense que vous devriez le faire. Vous voudrez peut-être d'abord tester le composant non connecté, mais je suggère que vous n'aurez pas une couverture de test complète sans également tester le composant connecté.

Voici un extrait non testé de ce que je fais avec Redux et Enzyme. L'idée centrale est d'utiliser Provider pour connecter l'état en test au composant connecté en test.

import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import SongForm from '../SongForm'; // import the CONNECTED component

// Use the same middlewares you use with Redux's applyMiddleware
const mockStore = configureMockStore([ /* middlewares */ ]);
// Setup the entire state, not just the part Redux passes to the connected component.
const mockStoreInitialized = mockStore({ 
    songs: { 
        songsList: {
            songs: {
                songTags: { /* ... */ } 
            }
        }
    }
}); 

const nullFcn1 = () => null;
const nullFcn2 = () => null;
const nullFcn3 = () => null;

const wrapper = mount( // enzyme
        <Provider store={store}>
          <SongForm
            screen="add"
            disabled={false}
            handleFormSubmit={nullFcn1}
            handleModifySong={nullFcn2}
            handleDeleteSong={nullFcn3}
          />
        </Provider>
      );

const formPropsFromReduxForm = wrapper.find(SongForm).props(); // enzyme
expect(
        formPropsFromReduxForm
      ).to.be.deep.equal({
        screen: 'add',
        songTags: initialSongTags,
        disabled: false,
        handleFormSubmit: nullFcn1,
        handleModifySong: nullFcn2,
        handleDeleteSong: nullFcn3,
      });

===== ../SongForm.js

import React from 'react';
import { connect } from 'react-redux';

const SongForm = (/* object */ props) /* ReactNode */ => {
    /* ... */
    return (
        <form onSubmit={handleSubmit(handleFormSubmit)}>
            ....
        </form>

};

const mapStateToProps = (/* object */ state) /* object */ => ({
    songTags: state.songs.songTags
});
const mapDispatchToProps = () /* object..function */ => ({ /* ... */ });

export default connect(mapStateToProps, mapDispatchToProps)(SongForm)

Vous voudrez peut-être créer un magasin avec Redux pur. redux-mock-store n'est qu'une version légère de celui-ci destiné aux tests.

Vous voudrez peut-être utiliser react-addons-test-utils au lieu de l'enzyme d'Airbnb.

J'utilise l'enzyme chai d'Airbnb pour avoir des options attendues réactives. Ce n'était pas nécessaire dans cet exemple.

14
JohnSz

Le problème avec la réponse acceptée est que nous exportons quelque chose inutilement juste pour pouvoir le tester. Et exporter une classe juste pour la tester n'est pas une bonne idée à mon avis.

Voici une solution plus nette sans avoir besoin d'exporter autre chose que le composant connecté:

Si vous utilisez jest, vous pouvez simuler la méthode connect pour retourner trois choses:

  1. mapStateToProps
  2. mapDispatchToProps
  3. ReactComponent

Cela est assez simple. Il existe 2 façons: les simulations en ligne ou les simulations globales.

1. Utilisation d'une maquette en ligne

Ajoutez l'extrait de code suivant avant la fonction de description du test.

jest.mock('react-redux', () => {
  return {
    connect: (mapStateToProps, mapDispatchToProps) => (ReactComponent) => ({
      mapStateToProps,
      mapDispatchToProps,
      ReactComponent
    }),
    Provider: ({ children }) => children
  }
})

2. Utilisation d'une maquette de fichier

  1. Créez un fichier __mocks__/react-redux.js À la racine (où se trouve package.json)
  2. Ajoutez l'extrait de code suivant dans le fichier.
module.exports = {
  connect: (mapStateToProps, mapDispatchToProps) => (ReactComponent) => ({
    mapStateToProps,
    mapDispatchToProps,
    ReactComponent,
  }),
  Provider: ({children}) => children
};

Après la moquerie, vous pourrez accéder à tous les trois ci-dessus en utilisant Container.mapStateToProps, Container.mapDispatchToProps Et Container.ReactComponent.

Le conteneur peut être importé en faisant simplement

import Container from '<path>/<fileName>.container.js'

J'espère que ça aide.

Notez que si vous utilisez une maquette de fichier. Le fichier simulé sera utilisé globalement pour tous les cas de test (sauf si vous faites jest.unmock('react-redux')) avant le cas de test.

Edit: J'ai écrit un blog détaillé expliquant en détail ce qui précède:

http://rahulgaba.com/front-end/2018/10/19/unit-testing-redux-containers-the-better-way-using-jest.html

7
Rahul Gaba

Essayez de créer 2 fichiers, un avec le composant lui-même, sans être au courant d'aucun magasin ou quoi que ce soit (PhoneVerification-component.js). Ensuite, le deuxième (PhoneVerification.js), que vous utiliserez dans votre application et qui ne renvoie que le premier composant abonné à stocker via la fonction connect, quelque chose comme

import PhoneVerificationComponent from './PhoneVerification-component.js'
import {connect} from 'react-redux'
...
export default connect(mapStateToProps, mapDispatchToProps)(PhoneVerificationComponent)

Ensuite, vous pouvez tester votre composant "stupide" en exigeant PhoneVerification-component.js dans votre test et en lui fournissant les accessoires moqués nécessaires. Il n'y a aucun point de test déjà testé (connect décorateur, mapStateToProps, mapDispatchToProps etc ...)

1
george.cz