2016-09-30 1 views
21

Я изучаю React, Babel, Semantic UI и Jest за последние пару недель. У меня на самом деле не слишком много проблем с моими компонентами, которые не отображаются в браузере, но у меня есть проблемы с рендерингом при написании модульных тестов с помощью Jest.Как я правильно издеваюсь над сторонними библиотеками (например, jQuery и Semantic UI) с помощью Jest?

тестируемой выглядит следующим образом:

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, status, err.toString()); 
      $('.toast').addClass('sad'); 
      $('.toast').html("Something bad happened: " + err.toString()); 
      $('.toast').transition('fade up', '500ms'); 
      setTimeout(function(){ 
       $('.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', 
       rules: [ 
        { 
         type: 'empty', 
         prompt: 'Please enter a last name.' 
        }, 
        { 
         type: 'doesntContain[<script>]', 
         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.' 
        }, 
        { 
         type: 'doesntContain[<script>]', 
         prompt: 'Please enter a valid email address.' 
        } 
       ] 
      }, 
      role: { 
       identifier: 'role', 
       rules: [ 
        { 
         type: 'empty', 
         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), 
     error: function(xhr, status, err) { 
     console.error(this.props.url, status, 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(); 
}); 

Вопрос, который я вижу, когда я бегу jest:

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) 
+0

Мне нужна компетентная помощь, награждающая эту награду, поскольку я ничего не знаю об этом предмете. Кто угодно? –

+0

Вот аналогичный вопрос, который может помочь: http://stackoverflow.com/questions/27189254/react-component-using-jquery-without-require-jest-unit-tests – Martina

ответ

2

Вы делаете это в правильном направлении, но одну простую ошибку.

Вы должны сказать шутку не издеваться JQuery

Чтобы было ясно,

из https://www.phpied.com/jest-jquery-testing-vanilla-app/ под 4 подзаголовком Тестирование Vanilla

[Он разговаривает о тестировании приложения Vanilla, но он прекрасно описывает около Jest]

Дело в Jest заключается в том, что он издевается над всем. Что бесценно для модульного тестирования. Но это также означает, что вам нужно объявить, когда вы не хотите, чтобы что-то насмехалось.

Это

jest.unmock(moduleName) 

Из документации Facebook,
unmock Указывает, что модульная система должна не возвращаться в высмеивал версию указанного модуля из требуют() (например, что он должен всегда возвращайте реальный модуль).

Наиболее распространенное использование этого API - это указание модуля, который данный тест намеревается тестировать (и, следовательно, не хочет автоматически издеваться).

Он возвращает объект шутки для цепочки.

Примечание: Ранее это было dontMock.

При использовании babel-jest вызовы разблокировки автоматически поднимаются в верхнюю часть блока кода.Используйте dontMock, если вы хотите явно избежать этого.
Здесь вы можете увидеть полную документацию Facebook's Documentation Page in Github.

Также необходимо использовать 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,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, status, err.toString()); 
      $('.toast').addClass('sad'); 
      $('.toast').html("Something bad happened: " + err.toString()); 
      $('.toast').transition('fade up', '500ms'); 
      setTimeout(function(){ 
       $('.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', 
       rules: [ 
        { 
         type: 'empty', 
         prompt: 'Please enter a last name.' 
        }, 
        { 
         type: 'doesntContain[<script>]', 
         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.' 
        }, 
        { 
         type: 'doesntContain[<script>]', 
         prompt: 'Please enter a valid email address.' 
        } 
       ] 
      }, 
      role: { 
       identifier: 'role', 
       rules: [ 
        { 
         type: 'empty', 
         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), 
     error: function(xhr, status, err) { 
     console.error(this.props.url, status, 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; 
0

В вашей конфигурации положить шутку ..

"setupFiles": ["./jestsetup.js"] 

В jestsetup.js вам нужно добавить $ и jQuery как глобальное ..

import $ from 'jquery'; 
global.$ = $; 
global.jQuery = $; 
Смежные вопросы