无法读取未定义单元测试酶的属性“contextTypes”
Posted
技术标签:
【中文标题】无法读取未定义单元测试酶的属性“contextTypes”【英文标题】:Cannot read property 'contextTypes' of undefined Unit Testing enzyme 【发布时间】:2017-06-16 12:38:06 【问题描述】:我正在使用 redux 应用程序在我的反应中尝试单元测试。所以我需要测试连接的组件不幸的是我收到了这个错误:
无法读取未定义的属性“contextTypes” 我在单元测试中使用酶这是我的组件:
import React from 'react';
import TextFieldGroup from '../common/TextFieldGroup';
import validateInput from '../../server/validations/login';
import connect from 'react-redux';
import login from '../../actions/authActions';
class LoginForm extends React.Component
constructor(props)
super(props);
this.state =
username: '',
password: '',
errors: ,
isLoading: false
;
this.onSubmit = this.onSubmit.bind(this);
this.onChange = this.onChange.bind(this);
isValid()
const errors, isValid = validateInput(this.state);
if (!isValid)
this.setState( errors );
return isValid;
onSubmit(e)
e.preventDefault();
if (this.isValid())
this.setState( errors: , isLoading: true );
this.props.login(this.state).then(
(res) => this.context.router.push('/'),
(err) => this.setState( errors: err.response.data.errors, isLoading: false )
);
onChange(e)
this.setState( [e.target.name]: e.target.value );
render()
const errors, username, password, isLoading = this.state;
return (
<form onSubmit=this.onSubmit>
<h1>Login</h1>
errors.form && <div className="alert alert-danger">errors.form</div>
<TextFieldGroup
field="username"
label="Username"
value=username
error=errors.username
onChange=this.onChange
/>
<TextFieldGroup
field="password"
label="Password"
value=password
error=errors.password
onChange=this.onChange
type="password"
/>
<div className="form-group"><button className="btn btn-primary" disabled=isLoading>Login</button></div>
</form>
);
LoginForm.propTypes =
login: React.PropTypes.func.isRequired
LoginForm.contextTypes =
router: React.PropTypes.object.isRequired
export default connect(null, login )(LoginForm);
这是我的测试:
import React from 'react';
import mount, shallow from 'enzyme';
import expect from 'chai';
import sinon from 'sinon';
import connect from 'react-redux'
import Login from '../../js/react/components/login/LoginForm';
describe('<Login />', function ()
it('should have an input for the username', function ()
const wrapper = shallow(<Login />);
expect(wrapper.find('input[name=username]')).to.have.length(1);
);
it('should have an input for the password', function ()
const wrapper = shallow(<Login />);
expect(wrapper.find('input[name=password]')).to.have.length(1);
);
it('should have a button', function ()
const wrapper = shallow(<Login />);
expect(wrapper.find('button')).to.have.length(1);
);
it('simulates click events', () =>
const onButtonClick = sinon.spy();
const wrapper = shallow(
<Login onButtonClick=onButtonClick />
);
wrapper.find('button').simulate('click');
expect(onButtonClick).to.have.property('callCount', 1);
);
);
非常感谢您的建议和答案:)
【问题讨论】:
已更改 >import Login from '../../js/react/components/login/LoginForm'; >从'../../js/react/components/login/LoginForm'导入登录;得到另一个错误>不变违规:在“Connect(LoginForm)”的上下文或道具中找不到“商店”。要么将根组件包装在您没有导出登录组件。含义:
class LoginForm extends React.Component ...
-> export class LoginForm extends React.Component ...
【讨论】:
【参考方案2】:测试装饰组件
要直接测试组件,您只需导出组件本身的函数而不将其传递给 Connect。
目前,在您的测试中,您正在导入由 connect() 返回的包装器组件,而不是 LoginForm 组件本身。如果您想测试 LoginForm 组件与 Redux 的交互,这很好。如果您没有单独测试组件,那么这种导出和导入组件的方法就可以了。但是,您需要记住使用专门为此单元测试创建的组件来包装测试中的组件。现在让我们看看这个我们正在测试一个连接组件的案例,并解释为什么我们将它包装在我们的单元测试中。
react-redux中Provider和Connect组件的关系
react-redux 库为我们提供了一个 Provider 组件。 Provider 的目的是允许其任何子组件在包装在 Connect 组件中时访问 Redux 存储。 Provider 和 Connect 之间的这种共生关系允许任何封装在 Connect 组件中的组件通过 React 的 context 功能访问 Redux 存储。
测试连接组件
还记得连接组件是包装在 Connect 组件中的组件,并且这种包装使我们的组件可以访问 Redux 存储吗?出于这个原因,我们需要在我们的测试文件中创建一个模拟存储,因为我们需要一个存储来测试组件如何与其交互。
在测试中为我们的 Connected 组件提供一个带有 Provider 的存储
但是,Connect 不知道如何神奇地访问商店。它需要嵌套(包装)在组件中。 Provider 组件通过 React 的 context api 挂钩到 Provider,从而使我们包装在 Connect 中的组件能够访问 Store。
正如我们所见,Provider 接受了一个由 Redux 存储组成的道具:
ReactDOM.render(
<Provider store=store>
<MyRootComponent />
</Provider>,
rootEl
)
所以要测试连接的组件,您需要用 Provider 包装它。请记住 Provider 将 Redux 存储对象作为道具。对于我们的测试,我们可以使用 redux-mock-store 库,它可以帮助我们建立一个 mock store,并在 store 上为我们提供一些方法来跟踪哪些 action 已被调度(如果我们需要它们)。
import Provider from 'react-redux'
import mount from 'enzyme'
import chai, expect from 'chai'
import chaiEnzyme from 'chai-enzyme'
import configureMockStore from 'redux-mock-store'
chai.use(chaiEnzyme())
import ConnectedLoginForm, from '../app.js'
const mockStore = configureMockStore(middlewares)
describe.only('<LoginForm Component />', () =>
it('LoginForm should pass a given prop to its child component' () =>
const store = mockStore(initialState)
const wrapper = mount(
<Provider store=store>
<ConnectedLoginForm />
</Provider>
)
expect(wrapper.type()).to.equal('div')
)
)
但有时您只想测试组件的渲染,而不需要 Redux 存储。让我们看看这个案例,我们要单独测试未连接的 LoginForm 组件的渲染。
测试未连接的组件
因此,目前您正在测试通过使用 Connect 包装原始 LoginForm 组件而创建的新组件。
为了在不连接的情况下测试原始组件本身,为 LoginForm 组件创建一个 单独的第二个导出声明。
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)
您现在可以在没有 Redux 存储的情况下测试组件的渲染。
在测试中导入组件
记住当你以这种方式导出组件时-
默认导出用于导入连接组件 为 导入未连接的组件现在在您的测试文件中导入未修饰的 LoginForm 组件,如下所示:
// Note the curly braces: grab the named export instead of default export
import LoginForm from './App'
或者导入未装饰和装饰(连接)的组件:
import ConnectedLoginForm, LoginForm from './App'
如果你想测试 LoginForm 组件如何与你的 Redux 存储交互,你必须
【讨论】:
以上是关于无法读取未定义单元测试酶的属性“contextTypes”的主要内容,如果未能解决你的问题,请参考以下文章
反应笑话单元测试用例TypeError:无法读取未定义的属性'then'
角度单元测试:TypeError:无法读取未定义的属性“根”
无法读取未定义的属性“getters” - 带有笑话的 VueJS 单元测试