SUT如下:
EditUser.jsx
var React = require('react'); var { browserHistory,Link } = require('react-router'); var $ = require('jquery'); import Navigation from '../Common/Navigation'; const apiUrl = process.env.API_URL; const phoneRegex = /^[(]{0,1}[0-9]{3}[)]{0,1}[-\s\.]{0,1}[0-9]{3}[-\s\.]{0,1}[0-9]{4}$/; var EditUser = React.createClass({ getInitialState: function() { return { email: '',firstName: '',lastName: '',phone: '',role: '' }; },handleSubmit: function(e) { e.preventDefault(); var data = { "email": this.state.email,"firstName": this.state.firstName,"lastName": this.state.lastName,"phone": this.state.phone,"role": this.state.role }; if($('.ui.form').form('is valid')) { $.ajax({ url: apiUrl + '/api/users/' + this.props.params.userId,dataType: 'json',contentType: 'application/json',type: 'PUT',data: JSON.stringify(data),success: function(data) { this.setState({data: data}); browserHistory.push('/Users'); $('.toast').addClass('happy'); $('.toast').html(data["firstName"] + ' ' + data["lastName"] + ' was updated successfully.'); $('.toast').transition('fade up','500ms'); setTimeout(function(){ $('.toast').transition('fade up','500ms').onComplete(function() { $('.toast').removeClass('happy'); }); },3000); }.bind(this),error: function(xhr,status,err) { console.error(this.props.url,err.toString()); $('.toast').addClass('sad'); $('.toast').html("Something bad happened: " + err.toString()); $('.toast').transition('fade up','500ms').onComplete(function() { $('.toast').removeClass('sad'); }); },3000); }.bind(this) }); } },handleChange: function(e) { var nextState = {}; nextState[e.target.name] = e.target.value; this.setState(nextState); },componentDidMount: function() { $('.dropdown').dropdown(); $('.ui.form').form({ fields: { firstName: { identifier: 'firstName',rules: [ { type: 'empty',prompt: 'Please enter a first name.' },{ type: 'doesntContain[<script>]',prompt: 'Please enter a valid first name.' } ] },lastName: { identifier: 'lastName',prompt: 'Please enter a last name.' },prompt: 'Please enter a valid last name.' } ] },email: { identifier: 'email',rules: [ { type: 'email',prompt: 'Please enter a valid email address.' },{ type: 'empty',prompt: 'Please enter an email address.' },prompt: 'Please enter a valid email address.' } ] },role: { identifier: 'role',prompt: 'Please select a role.' } ] },phone: { identifier: 'phone',optional: true,rules: [ { type: 'minLength[10]',prompt: 'Please enter a valid phone number of at least {ruleValue} digits.' },{ type: 'regExp',value: phoneRegex,prompt: 'Please enter a valid phone number.' } ] } } }); $.ajax({ url: apiUrl + '/api/users/' + this.props.params.userId,dataType:'json',cache: false,success: function(data) { this.setState({data: data}); this.setState({email: data.email}); this.setState({firstName: data.firstName}); this.setState({lastName: data.lastName}); this.setState({phone: data.phone}); this.setState({role: data.role}); }.bind(this),err) { console.error(this.props.url,err.toString()); }.bind(this) }); },render: function () { return ( <div className="container"> <Navigation active="Users"/> <div className="ui segment"> <h2>Edit User</h2> <div className="required warning"> <span className="red text">*</span><span> required</span> </div> <form className="ui form" onSubmit={this.handleSubmit} data={this.state}> <h4 className="ui dividing header">User Information</h4> <div className="ui three column grid field"> <div className="row fields"> <div className="column field required"> <label>First Name</label> <input type="text" name="firstName" value={this.state.firstName} onChange={this.handleChange}/> </div> <div className="column field required"> <label>Last Name</label> <input type="text" name="lastName" value={this.state.lastName} onChange={this.handleChange}/> </div> <div className="column field required"> <label>Email</label> <input type="text" name="email" value={this.state.email} onChange={this.handleChange}/> </div> </div> </div> <div className="ui three column grid field"> <div className="row fields"> <div className="column field required"> <label>User Role</label> <select className="ui dropdown" name="role" onChange={this.handleChange} value={this.state.role}> <option value="SuperAdmin">Super Admin</option> </select> </div> <div className="column field"> <label>Phone</label> <input name="phone" value={this.state.phone} onChange={this.handleChange}/> </div> </div> </div> <div className="ui three column grid"> <div className="row"> <div className="right floated column"> <div className="right floated large ui buttons"> <Link to="/Users" className="ui button">Cancel</Link> <button className="ui button primary" type="submit">Save</button> </div> </div> </div> </div> <div className="ui error message"></div> </form> </div> </div> ); } }); module.exports = EditUser;
相关的测试文件如下:
EditUser.test.js
var React = require('react'); var Renderer = require('react-test-renderer'); var jQuery = require('jquery'); require('../../../semantic/dist/components/dropdown'); import EditUser from '../../../app/components/Users/EditUser'; it('renders correctly',() => { const component = Renderer.create( <EditUser /> ).toJSON(); expect(component).toMatchSnapshot(); });
我跑的时候看到的问题:
FAIL test/components/Users/EditUser.test.js ● Test suite Failed to run ReferenceError: jQuery is not defined at Object.<anonymous> (semantic/dist/components/dropdown.min.js:11:21523) at Object.<anonymous> (test/components/Users/EditUser.test.js:6:370) at process._tickCallback (node.js:369:9)
解决方法
You have to tell jest not to mock jquery
要清楚,
from 07000 under 4th subtitle Testing Vanilla
[It talks about testing a Vanilla app,but it perfectly describe about Jest]
The thing about Jest is that it mocks everything. Which is priceless for unit testing. But it also means you need to declare when you don’t want something mocked.
那是
jest.unmock(moduleName)
From Facebook’s documentation
unmock
Indicates that the module system should never return a mocked version of the specified module from require() (e.g. that it should always return the real module).The most common use of this API is for specifying the module a given test intends to be testing (and thus doesn’t want automatically mocked).
It returns the jest object for chaining.
Note : PrevIoUsly it was
dontMock
.When using babel-jest,calls to unmock will automatically be hoisted to the top of the code block. Use dontMock if you want to explicitly avoid this behavior.
You can see the full documentation here 07001.
在require中也使用const而不是var。那是
const $ = require('jquery');
所以代码看起来像
jest.unmock('jquery'); // unmock it. In prevIoUs versions,use dontMock instead var React = require('react'); var { browserHistory,Link } = require('react-router'); const $ = require('jquery'); import Navigation from '../Common/Navigation'; const apiUrl = process.env.API_URL; const phoneRegex = /^[(]{0,render: function () { return ( <div className="container"> <Navigation active="Users"/> <div className="ui segment"> <h2>Edit User</h2> <div className="required warning"> <span className="red text">*</span><span> required</span> </div> <form className="ui form" onSubmit={this.handleSubmit} data={this.state}> <h4 className="ui dividing header">User Information</h4> <div className="ui three column grid field"> <div className="row fields"> <div className="column field required"> <label>First Name</label> <input type="text" name="firstName" value={this.state.firstName} onChange={this.handleChange}/> </div> <div className="column field required"> <label>Last Name</label> <input type="text" name="lastName" value={this.state.lastName} onChange={this.handleChange}/> </div> <div className="column field required"> <label>Email</label> <input type="text" name="email" value={this.state.email} onChange={this.handleChange}/> </div> </div> </div> <div className="ui three column grid field"> <div className="row fields"> <div className="column field required"> <label>User Role</label> <select className="ui dropdown" name="role" onChange={this.handleChange} value={this.state.role}> <option value="SuperAdmin">Super Admin</option> </select> </div> <div className="column field"> <label>Phone</label> <input name="phone" value={this.state.phone} onChange={this.handleChange}/> </div> </div> </div> <div className="ui three column grid"> <div className="row"> <div className="right floated column"> <div className="right floated large ui buttons"> <Link to="/Users" className="ui button">Cancel</Link> <button className="ui button primary" type="submit">Save</button> </div> </div> </div> </div> <div className="ui error message"></div> </form> </div> </div> ); } }); module.exports = EditUser;