- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 redux 应用程序在我的 React 中进行单元测试。所以我需要测试连接的组件,不幸的是我收到了这个错误:
Cannot read property 'contextTypes' of undefined I use enzyme in unit testing Here is my component:
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);
});
});
非常感谢建议和答案:)
最佳答案
测试装饰组件
要直接测试组件,您只需导出组件本身的函数,而不将其传递到 Connect。
当前,在您的测试中,您正在导入 connect() 返回的包装器组件,而不是 LoginForm 组件本身。如果您想测试 LoginForm 组件与 Redux 的交互,这很好。如果您不单独测试组件,那么这种导出和导入组件的方法就很好。但是,您需要记住使用专门为此单元测试创建的组件来包装测试中的组件。现在让我们看一下正在测试连接组件的情况,并解释为什么我们将其包装在单元测试中。
react-redux中Provider和Connect组件的关系
react-redux 库为我们提供了一个 Provider 组件。 Provider 的目的是允许其任何子组件在包装在 Connect 组件中时访问 Redux 存储。 Provider 和 Connect 之间的这种共生关系允许包装在 Connect 组件中的任何组件通过 React 的 context 访问 Redux 存储。功能。
测试连接的组件
还记得连接组件是包装在 Connect 组件中的组件,这种包装使我们的组件可以访问 Redux 存储吗?因此,我们需要在测试文件中创建一个模拟存储,因为我们需要一个存储来测试组件如何与其交互。
在测试中为我们的连接组件提供一个带有 Provider 的商店
但是 Connect 不知道如何神奇地访问商店。它需要嵌套(包装)在组件中。 Provider 组件通过 React 的 context api 连接到 Provider,为 Connect 中包装的组件提供对 Store 的访问权限。
正如我们所见,Provider 接受一个由 Redux 存储组成的 prop:
ReactDOM.render(
<Provider store={store}>
<MyRootComponent />
</Provider>,
rootEl
)
因此,要测试连接的组件,您需要用 Provider 包装它。请记住,Provider 将 Redux store 对象作为 prop。对于我们的测试,我们可以使用 redux-mock-store 库,它可以帮助我们设置一个模拟存储,并为我们提供一些存储上的方法来跟踪哪些操作已被调度(如果我们需要的话)。
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 存储交互,您必须
关于unit-testing - 无法读取未定义单元测试 enzyme 的属性 'contextTypes',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41951427/
你能比较一下属性吗 我想禁用文本框“txtName”。有两种方式 使用javascript,txtName.disabled = true 使用 ASP.NET, 哪种方法更好,为什么? 最佳答案 我
Count 属性 返回一个集合或 Dictionary 对象包含的项目数。只读。 object.Count object 可以是“应用于”列表中列出的任何集合或对
CompareMode 属性 设置并返回在 Dictionary 对象中比较字符串关键字的比较模式。 object.CompareMode[ = compare] 参数
Column 属性 只读属性,返回 TextStream 文件中当前字符位置的列号。 object.Column object 通常是 TextStream 对象的名称。
AvailableSpace 属性 返回指定的驱动器或网络共享对于用户的可用空间大小。 object.AvailableSpace object 应为 Drive 
Attributes 属性 设置或返回文件或文件夹的属性。可读写或只读(与属性有关)。 object.Attributes [= newattributes] 参数 object
AtEndOfStream 属性 如果文件指针位于 TextStream 文件末,则返回 True;否则如果不为只读则返回 False。 object.A
AtEndOfLine 属性 TextStream 文件中,如果文件指针指向行末标记,就返回 True;否则如果不是只读则返回 False。 object.AtEn
RootFolder 属性 返回一个 Folder 对象,表示指定驱动器的根文件夹。只读。 object.RootFolder object 应为 Dr
Path 属性 返回指定文件、文件夹或驱动器的路径。 object.Path object 应为 File、Folder 或 Drive 对象的名称。 说明 对于驱动器,路径不包含根目录。
ParentFolder 属性 返回指定文件或文件夹的父文件夹。只读。 object.ParentFolder object 应为 File 或 Folder 对象的名称。 说明 以下代码
Name 属性 设置或返回指定的文件或文件夹的名称。可读写。 object.Name [= newname] 参数 object 必选项。应为 File 或&
Line 属性 只读属性,返回 TextStream 文件中的当前行号。 object.Line object 通常是 TextStream 对象的名称。 说明 文件刚
Key 属性 在 Dictionary 对象中设置 key。 object.Key(key) = newkey 参数 object 必选项。通常是 Dictionary 
Item 属性 设置或返回 Dictionary 对象中指定的 key 对应的 item,或返回集合中基于指定的 key 的&
IsRootFolder 属性 如果指定的文件夹是根文件夹,返回 True;否则返回 False。 object.IsRootFolder object 应为&n
IsReady 属性 如果指定的驱动器就绪,返回 True;否则返回 False。 object.IsReady object 应为 Drive&nbs
FreeSpace 属性 返回指定的驱动器或网络共享对于用户的可用空间大小。只读。 object.FreeSpace object 应为 Drive 对象的名称。
FileSystem 属性 返回指定的驱动器使用的文件系统的类型。 object.FileSystem object 应为 Drive 对象的名称。 说明 可
Files 属性 返回由指定文件夹中所有 File 对象(包括隐藏文件和系统文件)组成的 Files 集合。 object.Files object&n
我是一名优秀的程序员,十分优秀!