有人可以告诉我如何测试一个类似这样的React组件事件处理程序吗?...
handleChange(e) {
this.setObjectState(e.target.getAttribute("for").toCamelCase(), e.target.value);
}
setObjectState(propertyName, value) {
let obj = this.state.registrationData;
obj[propertyName] = value;
this.setState({registrationData: obj});
}
我已经使用Enzyme编写了一个测试来测试渲染,并可以模拟事件以测试处理程序是否被调用,但这只是简单的部分。 我想测试事件代码运行时会发生什么。我可以手动触发它们,但我不知道在测试中应该传递什么到'e'参数。如果我使用Enzyme,则除非将事件处理程序存根为“ e”,否则测试将失败,因为“ e”未定义。
以下是Enzyme测试代码...
describe("uses internal state to", () => {
let stateStub = null;
let formWrapper = null;
beforeEach(() => {
wrapper = shallow(<RegistrationForm />);
instance = wrapper.instance();
stateStub = sinon.stub(instance, 'setState');
formWrapper = wrapper.find(Form.Wrapper);
});
afterEach(() => {
stateStub.restore();
});
it("handle changing an email address", () => {
formWrapper.find("[name='Email']").simulate('change')
sinon.called(stateStub);
})
});
我简要研究了使用“挂载”而不是“浅渲染”,但我根本无法运行它。它有很多问题,比如不能在执行之前存根查找下拉菜单的数据加载。
这是我理想情况下要尝试的内容...
describe("ALT uses internal state to", () => {
let stateStub = null;
let formWrapper = null;
beforeEach(() => {
wrapper = shallow(<RegistrationForm />);
instance = wrapper.instance();
stateStub = sinon.stub(instance, 'setState');
});
afterEach(() => {
stateStub.restore();
});
it("handle changing an email address", () => {
let e = 'some fake data - what IS this object?';
instance.handleChange(e);
sinon.called(stateStub);
sinon.calledWith({registrationData: errr.. what?});
})
});
以下是完整的组件代码:
import ErrorProcessor from "./error-processor";
import Form from "../../../../SharedJs/components/form/index.jsx"
import HiddenState from "../../data/hidden-state"
import LookupRestServiceGateway from "../../../../SharedJs/data/lookup-rest-service-gateway"
import React from "react";
import RegistrationRestServiceGateway from "../../data/registration-rest-service-gateway"
export default class RegistrationForm extends React.Component {
constructor(props) {
super(props);
this.state = props.defaultState || {
registrationData: {
email: "",
password: "",
confirmPassword: "",
firstName: "",
lastName: "",
employerID: null
},
registered: false,
employersLookupData: []
};
this.formId = "registration-form";
this.errorProcessor = new ErrorProcessor();
this.employersDataSource = new LookupRestServiceGateway(`/api/lookups/employers/${HiddenState.getServiceOperatorCode()}`);
this.registrationGateway = new RegistrationRestServiceGateway();
this.handleChange = this.handleChange.bind(this);
this.handleEmployerChange = this.handleEmployerChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setObjectState(e.target.getAttribute("for").toCamelCase(), e.target.value);
}
handleEmployerChange(e) {
this.setObjectState("EmployerID", e.target.value);
}
handleSubmit() {
this.submitRegistration();
}
componentDidMount() {
this.loadLookupData();
}
loadLookupData() {
this.employersDataSource.getListItems({ successCallback: (data) => {
this.setState({ employersLookupData: data ? data.items : [] });
}});
}
setObjectState(propertyName, value) {
let obj = this.state.registrationData;
obj[propertyName] = value;
this.setState({registrationData: obj});
}
submitRegistration() {
this.registrationGateway.register({
data: this.state.registrationData,
successCallback: (data, status, xhr) => {
this.setState({registered: true});
if (data.errors && data.errors.length) {
this.errorProcessor.processErrorObject(this.formId, xhr);
}
},
errorCallback: (xhr) => {
this.errorProcessor.processErrorObject(this.formId, xhr);
}
});
}
render() {
return (this.state.registered ? this.renderConfirmation() : this.renderForm());
}
renderConfirmation() {
return (
<div className = "registration-form">
<p>Your registration has been submitted. An email will be sent to you to confirm your registration details before you can log in.</p>
<Form.ErrorDisplay />
</div>
);
}
renderForm() {
return (
<Form.Wrapper formId = {this.formId}
className = "registration-form form-horizontal"
onSubmit = {this.handleSubmit}>
<h4>Create a new account.</h4>
<hr/>
<Form.ErrorDisplay />
<Form.Line name = "Email"
label = "Email"
type = "email"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
value = {this.state.registrationData.email}
onChange = {this.handleChange} />
<Form.Line name = "Password"
label = "Password"
type = "password"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
value = {this.state.registrationData.password}
onChange = {this.handleChange} />
<Form.Line name = "ConfirmPassword"
label = "Confirm Password"
type = "password"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
value = {this.state.registrationData.confirmPassword}
onChange = {this.handleChange} />
<Form.Line name = "FirstName"
label = "First Name"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
value = {this.state.registrationData.firstName}
onChange = {this.handleChange} />
<Form.Line name = "LastName"
label = "Last Name"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
value = {this.state.registrationData.lastName}
onChange = {this.handleChange} />
<Form.DropDownLine name = "EmployerID"
label = "Employer"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
emptySelection = "Please select an employer…"
onChange = {this.handleEmployerChange}
selectedValue = {this.state.registrationData.employerID}
items = {this.state.employersLookupData}/>
<Form.Buttons.Wrapper className="col-sm-offset-3 col-md-offset-2 col-md-10 col-sm-9">
<Form.Buttons.Submit text = "Register"
icon = "fa-user-plus" />
</Form.Buttons.Wrapper>
</Form.Wrapper>
);
}
}
RegistrationForm.PropTypes = {
defaultState: React.PropTypes.object
}
我现在已经设法让它工作了,但这感觉非常非常糟糕——这让我想到Enzyme的mount是正确的选择,但这也带来了很多问题,而且我的规范文件已经比我的组件大10倍,这一切似乎都毫无意义...
describe("uses internal state to", () => {
let stateStub = null;
let formWrapper = null;
beforeEach(() => {
wrapper = shallow(<RegistrationForm />);
instance = wrapper.instance();
formWrapper = wrapper.find(Form.Wrapper);
stateStub = sinon.stub(instance, 'setState');
});
afterEach(() => {
stateStub.restore();
});
it("handle changing an email address", () => {
$("body").append(`<input type="email" for="Email" id="field" class="form-control form-control " maxlength="10000" value="">`);
let node = $("#field")[0];
node.value = "new@ddress.co.uk";
instance.handleChange({target: node});
sinon.assert.called(stateStub);
})
});