2017-03-23 2 views
3

Kann jemand bitte sagen Sie mir, wie ein Test Reagieren Komponente Event-Handler, der so aussieht ...Testing Event-Handler

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}); 
} 

Ich habe einen Test geschrieben Enzym mit dem Rendering zu testen und können Ich simuliere die Ereignisse, um zu testen, dass die Handler tatsächlich aufgerufen werden, aber das ist der einfache Teil. Ich möchte testen, was passiert, wenn der Ereigniscode ausgeführt wird. Ich kann diese manuell auslösen, aber ich weiß nicht, was ich an den 'e' Parameter im Test weitergeben soll. Wenn ich Enzym verwende, fällt der Test um, es sei denn, ich stub den Event-Handler, da 'e' nicht definiert ist.

Hier ist der Enzymtest Bits ...

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); 
     }) 
    }); 

ich mit ‚Mount‘ anstelle von ‚flachen‘ kurz sah, aber ich konnte sie nicht auf Anhieb funktioniert bekommen. Das hat viele viele Probleme, wie zum Beispiel nicht in der Lage zu sein, Dinge wie die Datenlast für die Look-Up-Drop-Downs auszugeben, bevor es versucht, es auszuführen.

Hier ist, was ich bin versucht (im Idealfall) zu tun ...

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?}); 
     }) 
    }); 

Auf Wunsch ist hier die vollständige Komponentencode ...

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&hellip;" 
            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 
} 

Ich habe es geschafft, diese Funktion zu erhalten so wie jetzt, aber das fühlt sich sehr sehr bescheuert an - Es bringt mich dazu zu denken, dass Enzyms Mount der Weg ist, aber das bringt so viele Probleme mit sich und meine Spec-Datei ist schon 10 mal so groß wie meine Komponente und alles scheint ziemlich sinnlos auf dieser Ebene ...

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 = "[email protected]"; 
      instance.handleChange({target: node}); 
      sinon.assert.called(stateStub); 
     }) 
    }); 
+0

React stellt Testprogramme zur Verfügung, um den Click-Handler zu simulieren: https://facebook.github.io/react/docs/test-utils.html#simulate – chrisjlee

+1

Enzym tut das - ich benutze keinen Jest. Ich versuche auch nicht zu klicken. Die Jest Docs haben mir vielleicht eine Idee gegeben. Danke für den Link. –

Antwort

1

Sie können eine Aktion simulieren, um die Methode aufzurufen und beispielsweise den Zustand der Komponente nach der Simulation zu überprüfen.

class Foo extends React.Component { 
    constructor(props) { 
    super(props); 
    this.state = { count: 0 }; 
    } 

    render() { 
    const { count } = this.state; 
    return (
     <div> 
     <div className={`clicks-${count}`}> 
      {count} clicks 
     </div> 
     <a onClick={() => this.setState({ count: count + 1 })}> 
      Increment 
     </a> 
     </div> 
    ); 
    } 
} 

const wrapper = shallow(<Foo />); 

expect(wrapper.find('.clicks-0').length).to.equal(1); 
wrapper.find('a').simulate('click'); 
expect(wrapper.find('.clicks-1').length).to.equal(1); 

Wenn Sie Jest verwenden, versuchen Sie dieses Beispiel aus dem official documentation zu folgen.

Ich hoffe, es hilft.

+0

Ich habe das versucht - das Problem, das ich habe, ist nicht zu wissen, was in diesem Szenario an handleChange weitergegeben werden soll. –

+0

Versuchen Sie, eine Aktion zu simulieren und überprüfen Sie den Komponentenstatus nach der Simulation. –

+0

Schauen Sie sich dieses Beispiel an (http://airbnb.io/enzyme/docs/api/ShallowWrapper/simulate.html). –