web-dev-qa-db-fra.com

Test des composants imbriqués avec Enzyme dans React & Redux

J'ai un composant SampleComponent qui monte un autre "composant connecté" (c'est-à-dire container). Lorsque j'essaie de tester SampleComponent par mounting (car j'ai besoin de componentDidMount), j'obtiens le message d'erreur

Violation invariante: Impossible de trouver "magasin" dans le contexte ou les accessoires de "Connect (ContainerComponent)". Soit envelopper le composant racine dans un ou explicitement passer "magasin" comme accessoire à "Connect (ContainerComponent)".

Quelle est la meilleure façon de tester cela?

29
Detuned

Ce que j’ai essentiellement fait, c’est d’apporter mon magasin redux (et Provider) et de le placer dans un composant utilitaire comme suit:

export const CustomProvider = ({ children }) => {
  return (
    <Provider store={store}>
      {children}
    </Provider>
  );
};

alors, je mount la SampleComponent et lance des tests pour la comparer:

it('contains <ChildComponent/> Component', () => {
  const wrapper = mount(
    <CustomProvider>
      <SampleComponent {...defaultProps} />
    </CustomProvider>
  );
  expect(wrapper.find(ChildComponent)).to.have.length(1);
});
10
Detuned

Vous pouvez utiliser l'export de noms pour résoudre ce problème:

Tu aurais dû:

class SampleComponent extends React.Component{
...
   render(){
       <div></div>
   }
}

export default connect(mapStateToProps, mapDispatchToProps)(SampleComponent)

Vous pouvez ajouter une exportation avant le cours:

export class SampleComponent extends React.Component{

et importer ce composant sans magasin Redux:

import { SampleComponent } from 'your-path/SampleComponent';

Avec cette solution, vous n'avez pas besoin d'importer le magasin dans vos fichiers de test.

3
Alessander França

Option 1)

Vous pouvez encapsuler le composant conteneur avec le composant fournisseur de React-Redux dans votre test. Ainsi, avec cette approche, vous référencez réellement le magasin, vous le transmettez au fournisseur et composez votre composant à tester à l'intérieur. L'avantage de cette approche est que vous pouvez créer un magasin personnalisé pour le test. Cette approche est utile si vous souhaitez tester les parties de votre composant liées à Redux. 

Option 2)

Peut-être que vous ne vous souciez pas de tester les morceaux liés à Redux. Si vous souhaitez simplement tester le comportement du composant et les comportements liés à l'état local du composant, vous pouvez simplement ajouter une exportation nommée pour la version brute non connectée de votre composant. Et juste pour préciser quand vous ajoutez le mot-clé "export" à votre classe, vous dites que la classe peut maintenant être importée de 2 manières, avec des accolades {} ou non. Exemple:

export class MyComponent extends React.Component{ render(){ ... }}

...

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

plus tard sur votre fichier de test:

import MyComponent from 'your-path/MyComponent'; // it needs a store because you use "default export" with connect
import {MyComponent} from 'your-path/MyComponent'; // don't need store because you use "export" on top of your class.

J'espère aider quelqu'un.

2
darmis

Il existe également la possibilité d'utiliser redux-mock-store .

Un magasin factice pour tester les créateurs d’actions asynchrones et les middlewares Redux. Le magasin fictif créera un tableau d'actions envoyées servant de journal des actions pour les tests.

Le magasin fictif fournit les méthodes nécessaires sur l'objet magasin qui sont requises pour Redux . Vous pouvez spécifier des middlewares facultatifs et l'état initial de votre application.

import configureStore from 'redux-mock-store'

const middlewares = []
const mockStore = configureStore(middlewares)

const initialState = {}
const store = mockStore(initialState)

const wrapper = mount(<SampleComponent store={store}/>)
1
LazerBass

dans le but de rendre l'utilisation de la syntaxe de décorateur plus testable, j'ai ajouté ceci: https://www.npmjs.com/package/babel-plugin-undecorate

contribution:

@anyOldClassDecorator
export class AnyOldClass {
  @anyOldMethodDecorator
  method() {
    console.log('hello');   
  }
}

sortie:

@anyOldClassDecorator
export class AnyOldClass {
  @anyOldMethodDecorator
  method() {
    console.log('hello');   
  }
}

export class __undecorated__AnyOldClass {
  method() {
    console.log('hello');   
  }
}

J'espère que cela peut fournir un solide Option 3!

0
SirRodge