reactjs - test - Infracción invariable: no se pudo encontrar la "tienda" en el contexto ni en los accesorios de "Connect(SportsDatabase)"
test react redux with jest (5)
Como sugieren los docs oficiales de redux, también es mejor exportar el componente no conectado.
Para poder probar el componente de la aplicación en sí sin tener que lidiar con el decorador, le recomendamos que también exporte el componente sin decorar:
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)
Dado que la exportación predeterminada sigue siendo el componente decorado, la declaración de importación que se muestra arriba funcionará como antes, por lo que no tendrá que cambiar el código de su aplicación. Sin embargo, ahora puede importar los componentes de la aplicación sin decorar en su archivo de prueba de esta manera:
// Note the curly braces: grab the named export instead of default export
import { App } from ''./App''
Y si necesitas ambos:
import ConnectedApp, { App } from ''./App''
En la aplicación en sí, aún la importaría normalmente:
import App from ''./App''
Solo usaría la exportación con nombre para las pruebas.
Código completo aquí: https://gist.github.com/js08/0ec3d70dfda76d7e9fb4
Hola,
- Tengo una aplicación donde muestra diferentes plantillas para escritorio y dispositivos móviles en función del entorno de compilación.
- Puedo desarrollarlo con éxito donde necesito ocultar el menú de navegación de mi plantilla móvil.
- en este momento puedo escribir un caso de prueba en el que se obtienen todos los valores a través de los tipos y se representa correctamente
- pero no estoy seguro de cómo escribir los casos de prueba de la unidad cuando es móvil, no debería representar el componente de navegación.
- Lo intenté pero me enfrento a un error ... ¿puede decirme cómo solucionarlo?
- Código de abajo.
Caso de prueba
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 donde se debe escribir el caso de prueba
if (sports.build === ''mobile'') {
sportsNavigationComponent = <div />;
sportsSideMEnu = <div />;
searchComponent = <div />;
sportsPlayersWidget = <div />;
}
Error
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)
Cuando creamos una aplicación react-redux, deberíamos esperar ver una estructura donde en la parte superior tenemos la etiqueta
Provider
que tiene una instancia de una tienda redux.
Esa etiqueta de
Provider
luego representa su componente principal, vamos a llamarlo el componente de la
App
que a su vez representa todos los demás componentes dentro de la aplicación.
Aquí está la parte clave, cuando ajustamos un componente con la función
connect()
, esa función
connect()
espera ver algún componente padre dentro de la jerarquía que tiene la etiqueta
Provider
.
Entonces, la instancia en la que colocas la función
connect()
allí, buscará la jerarquía e intentará encontrar el
Provider
.
Eso es lo que quieres que suceda, pero en tu entorno de prueba ese flujo se está rompiendo.
¿Por qué?
¿Por qué?
Cuando volvamos al supuesto archivo de prueba sportsDatabase, debe ser el componente sportsDatabase por sí mismo y luego tratar de representar ese componente por sí mismo de forma aislada.
Entonces, esencialmente, lo que está haciendo dentro de ese archivo de prueba es simplemente tomar ese componente y arrojarlo en la naturaleza y no tiene vínculos con ningún
Provider
o tienda por encima y es por eso que está viendo este mensaje.
No hay una tienda o etiqueta de
Provider
en el contexto o accesorio de ese componente y, por lo tanto, el componente arroja un error porque quiere ver una etiqueta de
Provider
o tienda en su jerarquía principal.
Entonces eso es lo que significa ese error.
Es muy simple
Está intentando probar el componente contenedor generado al llamar a
connect()(MyPlainComponent)
.
Ese componente de envoltura espera tener acceso a una tienda Redux.
Normalmente esa tienda está disponible como
context.store
, porque en la parte superior de su jerarquía de componentes tendría un
<Provider store={myStore} />
.
Sin embargo, está renderizando su componente conectado por sí mismo, sin tienda, por lo que arroja un error.
Tienes algunas opciones:
-
Cree una tienda y renderice un
<Provider>
alrededor de su componente conectado -
Cree una tienda y páselo directamente como
<MyConnectedComponent store={store} />
, ya que el componente conectado también aceptará "store" como accesorio -
No te molestes en probar el componente conectado.
Exporte la versión "simple", sin conexión, y pruébelo en su lugar.
Si prueba su componente simple y su función
mapStateToProps
, puede asumir con seguridad que la versión conectada funcionará correctamente.
Probablemente desee leer la página "Pruebas" en los documentos de Redux: https://redux.js.org/recipes/writing-tests .
editar :
Después de ver que ha publicado la fuente y volver a leer el mensaje de error, el verdadero problema no está en el componente SportsTopPane.
El problema es que está intentando renderizar "completamente" SportsTopPane, que también representa a todos sus elementos secundarios, en lugar de hacer un renderizado "superficial" como lo fue en el primer caso.
La línea
searchComponent = <SportsDatabase sportsWholeFramework="desktop" />;
está representando un componente que supongo que también está conectado, y por lo tanto espera que una tienda esté disponible en la función "contexto" de React.
En este punto, tiene dos nuevas opciones:
- Solo realice renderizaciones "superficiales" de SportsTopPane, de modo que no lo obligue a renderizar completamente a sus hijos
- Si desea hacer una representación "profunda" de SportsTopPane, deberá proporcionar una tienda Redux en contexto. Le recomiendo que eche un vistazo a la biblioteca de pruebas de Enzyme, que le permite hacer exactamente eso. Consulte http://airbnb.io/enzyme/docs/api/ReactWrapper/setContext.html para ver un ejemplo.
En general, me gustaría señalar que podría estar tratando de hacer demasiado en este componente. Es posible que desee considerar dividirlo en partes más pequeñas con menos lógica por componente.
Posible solución que me funcionó con broma
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);
});
});
en mi caso solo
const myReducers = combineReducers({
user: UserReducer
});
const store: any = createStore(
myReducers,
applyMiddleware(thunk)
);
shallow(<Login />, { context: { store } });