此处的完整代码:https://gist.github.com/js08/0ec3d70dfda76d7e9fb4
您好,
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)
答案 0 :(得分:128)
非常简单。您正在尝试通过调用connect()(MyPlainComponent)
来测试生成的包装器组件。该包装器组件希望能够访问Redux存储。通常情况下,商店可以context.store
显示,因为在您的组件层次结构的顶部,您有<Provider store={myStore} />
。但是,您自己渲染连接的组件,没有商店,因此它会抛出错误。
您有几个选择:
<Provider>
<MyConnectedComponent store={store} />
传递,因为连接的组件也会接受&#34;存储&#34;作为道具mapStateToProps
功能,则可以安全地假设连接版本可以正常工作。您可能希望阅读&#34;测试&#34; Redux文档中的页面:https://redux.js.org/recipes/writing-tests。
修改强>:
在实际看到您发布了源代码并重新阅读错误消息后,真正的问题不在于SportsTopPane组件。问题是你要完全&#34;完全&#34;渲染SportsTopPane,它也渲染了它的所有孩子,而不是做一个&#34;浅&#34;像你在第一种情况下渲染。行searchComponent = <SportsDatabase sportsWholeFramework="desktop" />;
正在渲染我认为也连接的组件,因此期望商店在React&#34; context&#34;中可用。特征
此时,您有两个新选项:
总的来说,我会注意到在这个组件中可能会尝试做太多。您可能需要考虑将每个组件的逻辑分解为更小的部分。
答案 1 :(得分:46)
可能的解决方案对我来说很开心
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);
});
});
答案 2 :(得分:45)
正如redux的官方docs所建议的那样,更好地导出未连接的组件。
为了能够在不必处理装饰器的情况下测试App组件本身,我们建议您也导出未修饰的组件:
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)
由于默认导出仍然是装饰组件,因此上图所示的import语句将像以前一样工作,因此您不必更改应用程序代码。但是,您现在可以在测试文件中导入未修饰的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'
您只能使用命名导出进行测试。
答案 3 :(得分:6)
当我们将react-redux应用程序放在一起时,我们应该期望看到一个结构,在顶部具有Provider
标签,该标签具有redux存储的实例。
然后,该Provider
标记呈现您的父组件,将其称为App
组件,它依次呈现该应用程序内的所有其他组件。
这是关键部分,当我们使用connect()
函数包装组件时,connect()
函数希望看到带有Provider
标签的层次结构中的某些父组件。 / p>
因此,您在其中放置了connect()
函数的实例,它将查找层次结构并尝试找到Provider
。
这就是您想要发生的事情,但是在测试环境中,流程正在崩溃。
为什么?
为什么?
当我们回到假定的sportsDatabase测试文件时,您必须自己是sportsDatabase组件,然后尝试独立地单独呈现该组件。
因此,本质上,您在该测试文件中所做的只是获取该组件并将其扔掉而已,并且它与任何Provider
都没有关系,也不存储在其上方,这就是为什么您看到此消息的原因
该组件的上下文或属性中没有store或Provider
标签,因此该组件抛出错误是因为它想在其父层次结构中看到Provider
标签或存储。
这就是该错误的意思。
答案 4 :(得分:5)
就我而言
const myReducers = combineReducers({
user: UserReducer
});
const store: any = createStore(
myReducers,
applyMiddleware(thunk)
);
shallow(<Login />, { context: { store } });
答案 5 :(得分:4)
对我来说,这是进口问题,希望对您有所帮助。 WebStorm默认导入错误。
替换
import connect from "react-redux/lib/connect/connect";
使用
import {connect} from "react-redux";
答案 6 :(得分:2)
这是我升级时发生的事情。我不得不降级。
react-redux ^ 5.0.6→^ 7.1.3
答案 7 :(得分:1)
此操作是从“酶”中导入{浅,安装};
const store = mockStore({
startup: { complete: false }
});
describe("==== Testing App ======", () => {
const setUpFn = props => {
return mount(
<Provider store={store}>
<App />
</Provider>
);
};
let wrapper;
beforeEach(() => {
wrapper = setUpFn();
});
答案 8 :(得分:1)
在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'));