web-dev-qa-db-fra.com

Violation invariante: impossible de trouver "magasin" dans le contexte ou les accessoires de "Connect (SportsDatabase)"

Code complet ici: https://Gist.github.com/js08/0ec3d70dfda76d7e9fb4

Salut,

  • J'ai une application qui montre différents modèles pour les ordinateurs de bureau et les mobiles en fonction de l'environnement de construction.
  • Je parviens à le développer là où il me faut masquer le menu de navigation de mon modèle pour mobile.
  • maintenant je suis capable d'écrire un cas de test où il récupère toutes les valeurs à travers les propriétés et rend correctement
  • mais pas sûr de savoir comment écrire les cas de tests unitaires lorsque son mobile, il ne devrait pas rendre le composant de navigation.
  • J'ai essayé mais je suis confronté à une erreur ... pouvez-vous me dire comment y remédier?.
  • provding code ci-dessous.

Cas de test

import {expect} from 'chai';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import {SportsTopPortion} from '../../../src/components/sports-top-portion/sports-top-portion.jsx';
require('../../test-utils/dom');


describe('"sports-top-portion" Unit Tests', function() {
    let shallowRenderer = TestUtils.createRenderer();

    let sportsContentContainerLayout ='mobile';
    let sportsContentContainerProfile = {'exists': 'hasSidebar'};
    let sportsContentContainerAuthExchange = {hasValidAccessToken: true};
    let sportsContentContainerHasValidAccessToken ='test'; 

    it('should render correctly', () => {
        shallowRenderer.render(<SportsTopPortion sportsWholeFramework={sportsContentContainerLayout} sportsPlayers={sportsContentContainerProfile} sportsAuthentication={sportsContentContainerAuthExchange} sportsUpperBar={{activeSportsLink:'test'}} />);
        //shallowRenderer.render(<SportsTopPortion sportsWholeFramework={sportsContentContainerLayout} sportsPlayers={sportsContentContainerProfile} hasValidAccessToken={sportsContentContainerHasValidAccessToken}  />);

        let renderedElement = shallowRenderer.getRenderOutput();
        console.log("renderedElement------->" + JSON.stringify(renderedElement));

        expect(renderedElement).to.exist;
    });

    it('should not render sportsNavigationComponent when sports.build is mobile', () => {
        let sportsNavigationComponent = TestUtils.renderIntoDocument(<SportsTopPortion sportsWholeFramework={sportsContentContainerLayout} sportsPlayers={sportsContentContainerProfile} sportsAuthentication={sportsContentContainerAuthExchange} sportsUpperBar={{activeSportsLink:'test'}} />);
        console.log("sportsNavigationComponent------->" + JSON.stringify(sportsNavigationComponent));

        //let footnoteContainer = TestUtils.findRenderedDOMComponentWithClass(sportsNavigationComponent, 'linkPack--standard');

        //expect(footnoteContainer).to.exist;
    });

});

Extrait de code dans lequel le scénario de test doit être écrit

if (sports.build === 'mobile') {
    sportsNavigationComponent = <div />;
    sportsSideMEnu = <div />;
    searchComponent = <div />;
    sportsPlayersWidget = <div />;
}

Erreur

1) "sports-top-portion" Unit Tests should not render sportsNavigationComponent when sports.build is mobile:
     Invariant Violation: Could not find "store" in either the context or props of "Connect(SportsDatabase)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(SportsDatabase)".
      at Object.invariant [as default] (C:\sports-whole-page\node_modules\invariant\invariant.js:42:15)
      at new Connect (C:\sports-whole-page\node_modules\react-redux\lib\components\createConnect.js:135:33)
      at [object Object].ReactCompositeComponentMixin.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactCompositeComponent.js:148:18)
      at [object Object].wrapper [as mountComponent] (C:\sports-whole-page\node_modules\react\lib\ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactReconciler.js:37:35)
      at ReactDOMComponent.ReactMultiChild.Mixin.mountChildren (C:\sports-whole-page\node_modules\react\lib\ReactMultiChild.js:241:44)
      at ReactDOMComponent.Mixin._createContentMarkup (C:\sports-whole-page\node_modules\react\lib\ReactDOMComponent.js:591:32)
      at ReactDOMComponent.Mixin.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactDOMComponent.js:479:29)
      at Object.ReactReconciler.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactReconciler.js:37:35)
      at ReactDOMComponent.ReactMultiChild.Mixin.mountChildren (C:\sports-whole-page\node_modules\react\lib\ReactMultiChild.js:241:44)
      at ReactDOMComponent.Mixin._createContentMarkup (C:\sports-whole-page\node_modules\react\lib\ReactDOMComponent.js:591:32)
      at ReactDOMComponent.Mixin.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactDOMComponent.js:479:29)
      at Object.ReactReconciler.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactReconciler.js:37:35)
      at [object Object].ReactCompositeComponentMixin.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactCompositeComponent.js:225:34)
      at [object Object].wrapper [as mountComponent] (C:\sports-whole-page\node_modules\react\lib\ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactReconciler.js:37:35)
      at ReactDOMComponent.ReactMultiChild.Mixin.mountChildren (C:\sports-whole-page\node_modules\react\lib\ReactMultiChild.js:241:44)
      at ReactDOMComponent.Mixin._createContentMarkup (C:\sports-whole-page\node_modules\react\lib\ReactDOMComponent.js:591:32)
      at ReactDOMComponent.Mixin.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactDOMComponent.js:479:29)
      at Object.ReactReconciler.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactReconciler.js:37:35)
      at [object Object].ReactCompositeComponentMixin.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactCompositeComponent.js:225:34)
      at [object Object].wrapper [as mountComponent] (C:\sports-whole-page\node_modules\react\lib\ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactReconciler.js:37:35)
      at ReactDOMComponent.ReactMultiChild.Mixin.mountChildren (C:\sports-whole-page\node_modules\react\lib\ReactMultiChild.js:241:44)
      at ReactDOMComponent.Mixin._createContentMarkup (C:\sports-whole-page\node_modules\react\lib\ReactDOMComponent.js:591:32)
      at ReactDOMComponent.Mixin.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactDOMComponent.js:479:29)
      at Object.ReactReconciler.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactReconciler.js:37:35)
      at [object Object].ReactCompositeComponentMixin.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactCompositeComponent.js:225:34)
      at [object Object].wrapper [as mountComponent] (C:\sports-whole-page\node_modules\react\lib\ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactReconciler.js:37:35)
      at [object Object].ReactCompositeComponentMixin.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactCompositeComponent.js:225:34)
      at [object Object].wrapper [as mountComponent] (C:\sports-whole-page\node_modules\react\lib\ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (C:\sports-whole-page\node_modules\react\lib\ReactReconciler.js:37:35)
      at mountComponentIntoNode (C:\sports-whole-page\node_modules\react\lib\ReactMount.js:266:32)
      at ReactReconcileTransaction.Mixin.perform (C:\sports-whole-page\node_modules\react\lib\Transaction.js:136:20)
      at batchedMountComponentIntoNode (C:\sports-whole-page\node_modules\react\lib\ReactMount.js:282:15)
      at ReactDefaultBatchingStrategyTransaction.Mixin.perform (C:\sports-whole-page\node_modules\react\lib\Transaction.js:136:20)
      at Object.ReactDefaultBatchingStrategy.batchedUpdates (C:\sports-whole-page\node_modules\react\lib\ReactDefaultBatchingStrategy.js:62:19)
      at Object.batchedUpdates (C:\sports-whole-page\node_modules\react\lib\ReactUpdates.js:94:20)
      at Object.ReactMount._renderNewRootComponent (C:\sports-whole-page\node_modules\react\lib\ReactMount.js:476:18)
      at Object.wrapper [as _renderNewRootComponent] (C:\sports-whole-page\node_modules\react\lib\ReactPerf.js:66:21)
      at Object.ReactMount._renderSubtreeIntoContainer (C:\sports-whole-page\node_modules\react\lib\ReactMount.js:550:32)
      at Object.ReactMount.render (C:\sports-whole-page\node_modules\react\lib\ReactMount.js:570:23)
      at Object.wrapper [as render] (C:\sports-whole-page\node_modules\react\lib\ReactPerf.js:66:21)
      at Object.ReactTestUtils.renderIntoDocument (C:\sports-whole-page\node_modules\react\lib\ReactTestUtils.js:76:21)
      at Context.<anonymous> (C:/codebase/sports-whole-page/test/components/sports-top-portion/sports-top-portion-unit-tests.js:28:41)
      at callFn (C:\sports-whole-page\node_modules\mocha\lib\runnable.js:286:21)
      at Test.Runnable.run (C:\sports-whole-page\node_modules\mocha\lib\runnable.js:279:7)
      at Runner.runTest (C:\sports-whole-page\node_modules\mocha\lib\runner.js:421:10)
      at C:\sports-whole-page\node_modules\mocha\lib\runner.js:528:12
      at next (C:\sports-whole-page\node_modules\mocha\lib\runner.js:341:14)
      at C:\sports-whole-page\node_modules\mocha\lib\runner.js:351:7
      at next (C:\sports-whole-page\node_modules\mocha\lib\runner.js:283:14)
      at Immediate._onImmediate (C:\sports-whole-page\node_modules\mocha\lib\runner.js:319:5)
92
user6015171

C'est assez simple. Vous essayez de tester le composant wrapper généré en appelant connect()(MyPlainComponent). Ce composant wrapper s'attend à avoir accès à un magasin Redux. Normalement, ce magasin est disponible sous la forme context.store, car vous avez un <Provider store={myStore} /> au sommet de votre hiérarchie de composants. Cependant, vous effectuez le rendu de votre composant connecté lui-même, sans magasin, ce qui génère une erreur.

Vous avez quelques options:

  • Créez un magasin et rendez un <Provider> autour de votre composant connecté
  • Créez un magasin et transmettez-le directement en tant que <MyConnectedComponent store={store} />, car le composant connecté acceptera également "magasin" comme accessoire.
  • Ne vous fatiguez pas à tester le composant connecté. Exportez la version "brute" non connectée et testez-la à la place. Si vous testez votre composant brut et votre fonction mapStateToProps, vous pouvez supposer en toute sécurité que la version connectée fonctionnera correctement.

Vous voudrez probablement lire la page "Test" dans la documentation Redux: https://redux.js.org/recipes/writing-tests .

éditer :

Après avoir constaté que vous avez posté la source et relu le message d'erreur, le vrai problème ne concerne pas le composant SportsTopPane. Le problème est que vous essayez de rendre "totalement" le rendu de SportsTopPane, qui restitue également tous ses enfants, plutôt que de faire un rendu "superficiel" comme dans le premier cas. La ligne searchComponent = <SportsDatabase sportsWholeFramework="desktop" />; rend un composant qui, je suppose, est également connecté et s'attend donc à ce qu'un magasin soit disponible dans la fonction "contexte" de React.

À ce stade, vous avez deux nouvelles options:

  • Ne faites que le rendu "superficiel" de SportsTopPane, de sorte que vous ne l'obligiez pas à rendre pleinement ses enfants
  • Si vous souhaitez effectuer un rendu "profond" de SportsTopPane, vous devez fournir un magasin Redux en contexte. Je vous suggère fortement de consulter la bibliothèque de tests enzymatiques, qui vous permet de faire exactement cela. Voir http://airbnb.io/enzyme/docs/api/ReactWrapper/setContext.html pour un exemple.

Dans l’ensemble, j’aimerais noter que l’on essaie peut-être de faire trop dans ce seul élément. Vous voudrez peut-être envisager de le diviser en éléments plus petits avec moins de logique par composant.

135
markerikson

Solution possible qui a fonctionné pour moi avec plaisanterie

import React from "react";
import { shallow } from "enzyme";
import { Provider } from "react-redux";
import configureMockStore from "redux-mock-store";
import TestPage from "../TestPage";

const mockStore = configureMockStore();
const store = mockStore({});

describe("Testpage Component", () => {
    it("should render without throwing an error", () => {
        expect(
            shallow(
                <Provider store={store}>
                    <TestPage />
                </Provider>
            ).exists(<h1>Test page</h1>)
        ).toBe(true);
    });
});
60
codeislife

Comme le suggère le docs de redux, il est préférable d’exporter également le composant non connecté.

Afin de pouvoir tester le composant App lui-même sans avoir à traiter avec le décorateur, nous vous recommandons également d'exporter le composant non décoré:

import { connect } from 'react-redux'

// Use named export for unconnected component (for tests)
export class App extends Component { /* ... */ }
 
// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App)

Étant donné que l'exportation par défaut est toujours le composant décoré, l'instruction d'importation illustrée ci-dessus fonctionnera comme auparavant afin que vous n'ayez pas à modifier votre code d'application. Cependant, vous pouvez maintenant importer les composants de l'application non décorés dans votre fichier de test de la manière suivante:

// Note the curly braces: grab the named export instead of default export
import { App } from './App'

Et si vous avez besoin des deux:

import ConnectedApp, { App } from './App'

Dans l'application elle-même, vous l'importeriez toujours normalement:

import App from './App'

Vous utiliseriez uniquement l'exportation nommée pour les tests.

53
Vishal Gulati

Lorsque nous assemblons une application react-redux, nous devrions nous attendre à voir une structure dans laquelle se trouve en haut la balise Provider qui contient une instance d'un magasin redux.

Cette balise Provider restitue alors votre composant parent. Appelons-la le composant App qui rend tous les autres composants de l'application.

Voici la partie clé, lorsque nous encapsulons un composant avec la fonction connect(), cette fonction connect() s'attend à voir un composant parent dans la hiérarchie ayant la balise Provider.

Ainsi, si vous placez la fonction connect() dans celle-ci, elle recherchera la hiérarchie et tentera de trouver la Provider.

C’est ce que vous souhaitez, mais dans votre environnement de test, ce flux est en panne.

Pourquoi?

Pourquoi?

Lorsque nous revenons au fichier de test sportsDatabase supposé, vous devez être le composant sportsDatabase en lui-même, puis essayer de restituer ce composant seul.

Donc, ce que vous faites dans le fichier de test consiste essentiellement à prendre ce composant et à le jeter à l'état sauvage. Il n'a aucun lien avec aucune Provider ni aucun magasin au-dessus de celui-ci et c'est pourquoi vous voyez ce message.

Il n'y a pas de marqueur ni de balise Provider dans le contexte ou l'accessoire de ce composant; le composant renvoie donc une erreur car il souhaite voir une balise Provider ou un magasin dans sa hiérarchie parent.

C’est donc ce que cette erreur signifie.

6
Daniel

dans mon cas juste

const myReducers = combineReducers({
  user: UserReducer
});

const store: any = createStore(
  myReducers,
  applyMiddleware(thunk)
);

shallow(<Login />, { context: { store } });

5
jose920405