Violação invariável: não foi possível encontrar "loja" no contexto ou adereços de "Connect (SportsDatabase)"

142

Código completo aqui: https://gist.github.com/js08/0ec3d70dfda76d7e9fb4

Oi,

  • Eu tenho um aplicativo onde ele mostra modelos diferentes para desktop e dispositivos móveis com base no ambiente de construção.
  • Sou capaz de desenvolvê-lo onde preciso ocultar o menu de navegação do meu modelo para celular.
  • agora eu sou capaz de escrever um caso de teste em que ele busca todos os valores através dos proptypes e renderiza corretamente
  • mas não sabe como escrever os casos de teste de unidade, quando móvel, não deve renderizar o componente nav.
  • Eu tentei, mas estou enfrentando um erro ... você pode me dizer como corrigi-lo.
  • código de comprovação abaixo.

Caso de teste

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

});

Fragmento de código em que o caso de teste precisa ser gravado

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

Erro

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)

fonte

Respostas:

182

É bem simples. Você está tentando testar o componente do wrapper gerado pela chamada connect()(MyPlainComponent). Esse componente do wrapper espera ter acesso a um repositório Redux. Normalmente essa loja está disponível como context.store, porque no topo da hierarquia de componentes você teria um <Provider store={myStore} />. No entanto, você está renderizando o componente conectado por si só, sem armazenamento, por isso está gerando um erro.

Você tem algumas opções:

  • Crie uma loja e renderize <Provider>ao redor do componente conectado
  • Crie uma loja e passe-a diretamente como <MyConnectedComponent store={store} />, pois o componente conectado também aceitará "loja" como suporte
  • Não se preocupe em testar o componente conectado. Exporte a versão "simples" não conectada e teste-a. Se você testar seu componente comum e sua mapStateToPropsfunção, poderá assumir com segurança que a versão conectada funcionará corretamente.

Você provavelmente deseja ler a página "Teste" nos documentos do Redux: https://redux.js.org/recipes/writing-tests .

editar :

Depois de realmente ver que você postou a fonte e reler a mensagem de erro, o verdadeiro problema não está no componente SportsTopPane. O problema é que você está tentando renderizar "totalmente" o SportsTopPane, que também renderiza todos os seus filhos, em vez de fazer uma renderização "superficial" como se estivesse no primeiro caso. A linha searchComponent = <SportsDatabase sportsWholeFramework="desktop" />;está renderizando um componente que eu suponho que também esteja conectado e, portanto, espera que uma loja esteja disponível no recurso "contexto" do React.

Neste ponto, você tem duas novas opções:

  • Faça apenas a renderização "superficial" do SportsTopPane, para que você não o force a renderizar totalmente seus filhos
  • Se você deseja fazer uma renderização "profunda" do SportsTopPane, precisará fornecer um armazenamento Redux no contexto. Eu sugiro que você dê uma olhada na biblioteca de testes de enzimas, que permite fazer exatamente isso. Consulte http://airbnb.io/enzyme/docs/api/ReactWrapper/setContext.html para obter um exemplo.

No geral, gostaria de observar que você pode estar tentando fazer muito neste componente e pode considerar dividi-lo em pedaços menores com menos lógica por componente.

markerikson
fonte
Eu tentei, mas não sei como fazer ... você pode atualizar em meus casos de teste
1
Estou assumindo que no SportsTopPortion.js, você tem let SportsTopPortion = connect(mapStateToProps)(SomeOtherComponent). A resposta mais fácil é testar esse outro componente, não o componente retornado por connect.
27416 Marker
1
Aha. Agora eu vejo o que está acontecendo. O problema não está no próprio SportsTopPane. O problema é que você está fazendo uma renderização "completa" do SportsTopPane, não uma renderização "superficial"; portanto, o React está tentando renderizar totalmente todos os filhos. A mensagem de erro refere-se à linha searchComponent = <SportsDatabase sportsWholeFramework="desktop" />;. Esse é o componente conectado que está esperando uma loja e quebrando. Portanto, duas novas sugestões: ou faça apenas uma renderização superficial do SportsTopPane ou use uma biblioteca como Enzyme para testar. Consulte airbnb.io/enzyme/docs/api/ReactWrapper/setContext.html .
27416 Marker
Você pode me dizer como escrever o caso de teste para este cenário `` `` mas não sabe como escrever os casos de teste de unidade quando seu celular não deve renderizar o componente nav. ``
3
Responder a alguém que está preso ou intrigado com a frase "é bastante simples" pode parecer depreciativo ou severo. Por favor, use com moderação.
Jayqui 6/05/19
97

Possível solução que funcionou para mim com brincadeira

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);
    });
});
codeislife
fonte
1
funciona bem, em vez de ter que passar adereços um por um.
Ghostkraviz
2
Obrigado, uma solução muito boa. Eu tive esse problema porque estou usando um componente de aplicativo de nível superior com roteamento e a loja é fornecida ao aplicativo filho em cada rota, para que eu não precise passar acessórios no roteador. Mudei um pouco para o meu uso. invólucro const = superficial (<Provider store = {store}> <App /> </Provider>); expect (wrapper.contains (<App />)).toBe(true);
Little Brain
69

Como sugerem os documentos oficiais do redux, é melhor exportar também o componente não conectado.

Para poder testar o próprio componente App sem precisar lidar com o decorador, recomendamos que você exporte também o componente não decorado:

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)

Como a exportação padrão ainda é o componente decorado, a instrução de importação mostrada acima funcionará como antes, para que você não precise alterar o código do aplicativo. No entanto, agora você pode importar os componentes de aplicativos não decorados em seu arquivo de teste, como este:

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

E se você precisar de ambos:

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

No próprio aplicativo, você ainda o importaria normalmente:

import App from './App'

Você usaria apenas a exportação nomeada para testes.

Vishal Gulati
fonte
1
Essa resposta também é legítima. Editou seu link para corresponder à âncora.
Erowlin
Esta resposta faz todo o sentido! Pode não ser a coisa certa em todos os casos, mas definitivamente melhor do que jogar com o provedor e tudo isso quando não é necessário.
Lokori
Obrigado @lokori Feliz que você gostou!
Vishal Gulati
2
Essa foi a maneira mais rápida e simples de fazer meu teste passar novamente.
Mike Lyons
2
"Você usaria apenas a exportação nomeada para testes." -- Funciona para mim.
#
7

Quando montamos um aplicativo react-redux, devemos esperar uma estrutura em que, no topo, tenhamos a Providertag que possui uma instância de uma loja de redux.

Essa Providertag então renderiza seu componente pai, vamos chamá-lo de Appcomponente que, por sua vez, renderiza todos os outros componentes dentro do aplicativo.

Aqui está a parte principal, quando envolvemos um componente com a connect()função, essa connect()função espera ver algum componente pai dentro da hierarquia que possui a Providertag.

Portanto, na instância em que você coloca a connect()função, ela pesquisa a hierarquia e tenta encontrar a Provider.

É isso que você deseja que aconteça, mas em seu ambiente de teste esse fluxo está quebrando.

Por quê?

Por quê?

Quando voltamos ao arquivo de teste sportsDatabase assumido, você deve ser o componente sportsDatabase sozinho e, em seguida, tentar renderizar esse componente sozinho.

Então, essencialmente, o que você está fazendo dentro desse arquivo de teste é apenas pegar esse componente e jogá-lo no ar, e ele não tem vínculos com nenhum Providerdeles ou é armazenado acima dele, e é por isso que você está vendo esta mensagem.

Não há armazenamento ou Providermarca no contexto ou suporte desse componente e, portanto, o componente gera um erro porque deseja ver uma Providermarca ou armazenamento em sua hierarquia pai.

Então é isso que esse erro significa.

Daniel
fonte
6

no meu caso apenas

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

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

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

jose920405
fonte
2

apenas faça essa importação {superficial, monte} de "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();
  });
Hilal Aissani
fonte
2

Para mim, foi um problema de importação, espero que ajude. a importação padrão do WebStorm estava errada.

substituir

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

com

import {connect} from "react-redux";
ATQSHL
fonte
1

Isso aconteceu comigo quando eu atualizei. Eu tive que voltar atrás.

Reação-redux ^ 5.0.6 → ^ 7.1.3

codie
fonte
Isso é mais um comentário do que uma resposta
sudo97 12/02
Houve muitas mudanças de última hora. Eu recomendo assistir este vídeo para entender melhor as alterações youtube.com/watch?v=yOZ4Ml9LlWE
Kamil Dzieniszewski
0

no final do seu Index.js, você precisa adicionar este código:

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

Mehrdad
fonte