Полный код здесь: https://gist.github.com/js08/0ec3d70dfda76d7e9fb4
Здравствуй,
- У меня есть приложение, в котором показаны разные шаблоны для настольных и мобильных устройств в зависимости от среды сборки.
- Я успешно могу разработать его там, где мне нужно скрыть меню навигации для моего мобильного шаблона.
- прямо сейчас я могу написать один тестовый пример, в котором он извлекает все значения через proptypes и правильно отображает
- но не уверен, как писать примеры модульного тестирования, когда его мобильный телефон не должен отображать компонент навигации.
- Я пробовал, но у меня возникла ошибка ... Подскажите, как ее исправить.
- код ниже.
Прецедент
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;
});
});
Фрагмент кода, в котором нужно написать тестовый пример
if (sports.build === 'mobile') {
sportsNavigationComponent = <div />;
sportsSideMEnu = <div />;
searchComponent = <div />;
sportsPlayersWidget = <div />;
}
ошибка
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)
let SportsTopPortion = connect(mapStateToProps)(SomeOtherComponent)
. Самый простой ответ - протестировать этот другой компонент, а не компонент, возвращаемыйconnect
.searchComponent = <SportsDatabase sportsWholeFramework="desktop" />;
. Это подключенный компонент, который ожидает выхода из строя. Итак, два новых предложения: либо делайте только поверхностный рендеринг SportsTopPane, либо используйте для тестирования библиотеку, такую как Enzyme. См. Airbnb.io/enzyme/docs/api/ReactWrapper/setContext.html .Возможное решение, которое сработало для меня в шутку
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); }); });
источник
Как предлагают официальные документы redux, лучше экспортировать и неподключенный компонент.
Чтобы иметь возможность протестировать сам компонент приложения без необходимости иметь дело с декоратором, мы рекомендуем вам также экспортировать недекорированный компонент:
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)
Поскольку экспорт по умолчанию по-прежнему является декорированным компонентом, оператор импорта, изображенный выше, будет работать, как и раньше, поэтому вам не придется изменять код приложения. Однако теперь вы можете импортировать недекорированные компоненты приложения в свой тестовый файл следующим образом:
// Note the curly braces: grab the named export instead of default export import { App } from './App'
И если вам нужны оба:
import ConnectedApp, { App } from './App'
В самом приложении вы все равно импортируете его в обычном режиме:
import App from './App'
Вы должны использовать указанный экспорт только для тестов.
источник
Когда мы собираем приложение response-redux, мы должны ожидать увидеть структуру, в которой наверху у нас есть
Provider
тег, который имеет экземпляр хранилища redux.Provider
Затем этот тег отображает ваш родительский компонент, позволяет называть егоApp
компонентом, который, в свою очередь, отображает все остальные компоненты внутри приложения.Вот ключевая часть: когда мы заключаем компонент в оболочку с
connect()
функцией, этаconnect()
функция ожидает увидеть какой-либо родительский компонент в иерархии, имеющейProvider
тег.Таким образом, экземпляр, в который вы помещаете
connect()
функцию, будет искать в иерархии и пытаться найтиProvider
.Это то, что вы хотите, но в вашей тестовой среде этот поток не работает.
Зачем?
Зачем?
Когда мы вернемся к предполагаемому тестовому файлу sportsDatabase, вы должны быть самим компонентом sportsDatabase, а затем пытаться визуализировать этот компонент отдельно.
По сути, то, что вы делаете внутри этого тестового файла, - это просто взять этот компонент и просто выбросить его на волю, и он не имеет никаких связей ни с чем
Provider
или хранится над ним, и именно поэтому вы видите это сообщение.В
Provider
контексте или опоре этого компонента нет хранилища или тега, поэтому компонент выдает ошибку, потому что он хочет увидетьProvider
тег или хранилище в своей родительской иерархии.Вот что означает эта ошибка.
источник
в моем случае просто
const myReducers = combineReducers({ user: UserReducer }); const store: any = createStore( myReducers, applyMiddleware(thunk) );
shallow(<Login />, { context: { store } });
источник
Для меня это была проблема с импортом, надеюсь, это поможет. Импорт по умолчанию WebStorm был неправильным.
заменить
import connect from "react-redux/lib/connect/connect";
с участием
import {connect} from "react-redux";
источник
jus сделайте это import {shallow, mount} из "фермента";
const store = mockStore({ startup: { complete: false } }); describe("==== Testing App ======", () => { const setUpFn = props => { return mount( <Provider store={store}> <App /> </Provider> ); }; let wrapper; beforeEach(() => { wrapper = setUpFn(); });
источник
Это случилось со мной, когда я обновился. Мне пришлось вернуться на более раннюю версию.
реакция-редукция ^ 5.0.6 → ^ 7.1.3
источник
в конце вашего Index.js необходимо добавить этот код:
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'));
источник