Violazione invariante: impossibile trovare "store" nel contesto o nei puntelli di "Connect (SportsDatabase)"


142

Codice completo qui: https://gist.github.com/js08/0ec3d70dfda76d7e9fb4

Ciao,

  • Ho un'applicazione in cui mostra diversi modelli per desktop e dispositivi mobili in base all'ambiente di costruzione.
  • Sono in grado di svilupparlo con successo dove devo nascondere il menu di navigazione per il mio modello di cellulare.
  • in questo momento sono in grado di scrivere un caso di prova in cui recupera tutti i valori attraverso i proptype e li rende correttamente
  • ma non sono sicuro di come scrivere i casi di unit test quando il suo dispositivo mobile non dovrebbe rendere il componente di navigazione.
  • Ho provato ma sto riscontrando un errore ... puoi dirmi come risolverlo.
  • codice provocatorio di seguito.

Caso di prova

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;
    });

});

Snippet di codice in cui è necessario scrivere il test case

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

Errore

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)

Risposte:


182

È abbastanza semplice Stai provando a testare il componente wrapper generato chiamando connect()(MyPlainComponent). Quel componente wrapper prevede di avere accesso a un negozio Redux. Normalmente quel negozio è disponibile come context.store, perché nella parte superiore della gerarchia dei componenti avresti un <Provider store={myStore} />. Tuttavia, stai eseguendo il rendering del componente connesso da solo, senza archivio, quindi viene generato un errore.

Hai alcune opzioni:

  • Creare un negozio e renderizzare un <Provider>intorno al componente collegato
  • Crea un negozio e passalo direttamente come <MyConnectedComponent store={store} />, poiché il componente collegato accetterà anche "store" come prop
  • Non preoccuparti di testare il componente collegato. Esporta la versione "semplice", non connessa, e prova invece quella. Se testate il vostro componente semplice e la vostra mapStateToPropsfunzione, potete tranquillamente presumere che la versione connessa funzionerà correttamente.

Probabilmente vorrai leggere la pagina "Test" nei documenti Redux: https://redux.js.org/recipes/writing-tests .

modifica :

Dopo aver effettivamente visto che hai postato la fonte e riletto il messaggio di errore, il vero problema non è con il componente SportsTopPane. Il problema è che stai provando a renderizzare "completamente" SportsTopPane, il che rende anche tutti i suoi figli, piuttosto che fare un rendering "superficiale" come nel primo caso. La linea searchComponent = <SportsDatabase sportsWholeFramework="desktop" />;sta eseguendo il rendering di un componente che presumo sia anch'esso connesso e pertanto si aspetta che un negozio sia disponibile nella funzione "contesto" di React.

A questo punto, hai due nuove opzioni:

  • Esegui solo il rendering "superficiale" di SportsTopPane, in modo da non forzarlo a renderlo completamente figlio
  • Se si desidera eseguire il rendering "approfondito" di SportsTopPane, è necessario fornire un negozio Redux nel contesto. Consiglio vivamente di dare un'occhiata alla libreria di test Enzyme, che consente di fare esattamente questo. Vedi http://airbnb.io/enzyme/docs/api/ReactWrapper/setContext.html per un esempio.

Nel complesso, vorrei notare che potresti provare a fare troppo in questo componente e potresti prendere in considerazione l'idea di suddividerlo in pezzi più piccoli con meno logica per componente.


Ho provato ma non sono sicuro di come fare ... è possibile aggiornare nei miei casi di test

1
Suppongo che in SportsTopPortion.js, hai let SportsTopPortion = connect(mapStateToProps)(SomeOtherComponent). La risposta più semplice è testare quell'altro componente, non il componente restituito da connect.
Markerikson,

1
Aha. Ora vedo cosa sta succedendo. Il problema non è con SportsTopPane stesso. Il problema è che stai eseguendo un rendering "completo" di SportsTopPane, non un rendering "superficiale", quindi React sta cercando di renderizzare completamente tutti i bambini. Il messaggio di errore si riferisce alla riga searchComponent = <SportsDatabase sportsWholeFramework="desktop" />;. Questo è il componente connesso che si aspetta un negozio e si rompe. Quindi, due nuovi suggerimenti: o esegui solo un rendering superficiale di SportsTopPane o usa una libreria come Enzyme per testare. Vedi airbnb.io/enzyme/docs/api/ReactWrapper/setContext.html .
Markerikson,

puoi dirmi come scrivere il caso di test per questo scenario `` '' ma non sono sicuro di come scrivere i casi di test unitari quando il suo dispositivo mobile non dovrebbe rendere il componente di navigazione. ``

3
Rispondere a qualcuno che è bloccato o perplesso con la frase "è piuttosto semplice" può venire come denigratorio o aspro. Si prega di usare con parsimonia.
jayqui,

97

Possibile soluzione che ha funzionato per me con jest

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);
    });
});

1
funziona bene, invece di dover passare oggetti di scena uno per uno.
ghostkraviz,

2
Grazie, una soluzione molto bella. Ho avuto questo problema perché sto usando un componente dell'app di livello superiore con routing e l'archivio è fornito all'app figlio in ogni percorso, quindi non devo passare oggetti di scena nel router. L'ho cambiato un po 'per il mio uso. const wrapper = shallow (<Provider store = {store}> <App /> </Provider>); prevedono (wrapper.contains (<App />)).toBe(true);
Little Brain,

69

Come suggeriscono i documenti ufficiali di Redux, è meglio esportare anche il componente non collegato.

Per poter testare il componente App stesso senza dover trattare con il decoratore, ti consigliamo di esportare anche il componente non decorato:

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)

Poiché l'esportazione predefinita è ancora il componente decorato, la dichiarazione di importazione nella foto sopra funzionerà come prima, quindi non dovrai cambiare il codice dell'applicazione. Tuttavia, ora puoi importare i componenti dell'app non decorati nel tuo file di test in questo modo:

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

E se hai bisogno di entrambi:

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

Nell'app stessa, la importeresti normalmente:

import App from './App'

Utilizzeresti solo l'esportazione denominata per i test.


1
Anche questa risposta è legittima. Modificato il tuo link per abbinare l'ancora.
Erowlin,

Questa risposta ha perfettamente senso! Potrebbe non essere la cosa giusta in tutti i casi, ma sicuramente meglio che giocare con il Provider e tutto ciò quando non è necessario.
lokori,

Grazie @lokori Felice che ti piaccia!
Vishal Gulati,

2
Questo è stato il modo più rapido e semplice per ripetere il test.
Mike Lyons,

2
"Utilizzeresti solo l'esportazione denominata per i test." -- Per me va bene.
technazi

7

Quando mettiamo insieme un'applicazione di reattivo-redux dovremmo aspettarci di vedere una struttura in cui nella parte superiore abbiamo il Providertag che ha un'istanza di un archivio di redux.

Quel Providertag esegue quindi il rendering del componente principale, chiamandolo il Appcomponente che a sua volta esegue il rendering di ogni altro componente all'interno dell'applicazione.

Ecco la parte chiave, quando avvolgiamo un componente con la connect()funzione, quella connect()funzione prevede di vedere alcuni componenti principali all'interno della gerarchia che ha il Providertag.

Quindi l'istanza in cui hai inserito la connect()funzione, cercherà la gerarchia e proverà a trovare il file Provider.

Questo è quello che vuoi che accada, ma nel tuo ambiente di test quel flusso si sta interrompendo.

Perché?

Perché?

Quando torniamo al file di prova sportsDatabase presunto, devi essere il componente sportsDatabase da solo e quindi provare a renderlo da solo in modo isolato.

Quindi essenzialmente quello che stai facendo all'interno di quel file di test è semplicemente prendere quel componente e buttarlo via in libertà e non ha legami con nessuno Providero archiviarlo sopra ed è per questo che vedi questo messaggio.

Non c'è archivio o Providertag nel contesto o prop di quel componente e quindi il componente genera un errore perché vuole vedere un Providertag o un archivio nella sua gerarchia padre.

Questo è ciò che significa quell'errore.


6

nel mio caso solo

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

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

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


2

jus fare questa importazione {shallow, mount} da "enzima";

const store = mockStore({
  startup: { complete: false }
});

describe("==== Testing App ======", () => {
  const setUpFn = props => {
    return mount(
      <Provider store={store}>
        <App />
      </Provider>
    );
  };

  let wrapper;
  beforeEach(() => {
    wrapper = setUpFn();
  });

2

Per me è stato un problema di importazione, spero che sia d'aiuto. l'importazione predefinita da WebStorm era errata.

sostituire

import connect from "react-redux/lib/connect/connect";

con

import {connect} from "react-redux";


0

alla fine di Index.js è necessario aggiungere questo codice:

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter  } from 'react-router-dom';

import './index.css';
import App from './App';

import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import thunk from 'redux-thunk';

///its your redux ex
import productReducer from './redux/reducer/admin/product/produt.reducer.js'

const rootReducer = combineReducers({
    adminProduct: productReducer
   
})
const composeEnhancers = window._REDUX_DEVTOOLS_EXTENSION_COMPOSE_ || compose;
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));


const app = (
    <Provider store={store}>
        <BrowserRouter   basename='/'>
            <App />
        </BrowserRouter >
    </Provider>
);
ReactDOM.render(app, document.getElementById('root'));

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.