Tidak dapat menemukan tombol untuk mensimulasikan klik - Unit Testing React w/ Mocha, Chai, Enzyme

Saya mencoba mensimulasikan klik tombol dengan Enzim. Saya sudah bisa menulis tes sederhana jika suatu elemen dirender, namun tes menyenangkan seperti klik tombol dll. gagal.

Dalam contoh ini kesalahan di terminal adalah:

 1) Front End @Profile Component clicks a button :
 Error: This method is only meant to be run on single node. 0 found instead.
  at ShallowWrapper.single (node_modules/enzyme/build/ShallowWrapper.js:1093:17)
  at ShallowWrapper.props (node_modules/enzyme/build/ShallowWrapper.js:532:21)
  at ShallowWrapper.prop (node_modules/enzyme/build/ShallowWrapper.js:732:21)
  at ShallowWrapper.simulate (node_modules/enzyme/build/ShallowWrapper.js:505:28)
  at Context.<anonymous> (cmpnt-profile.spec.js:36:32)

tes unitnya adalah:

      describe('Front End @Profile Component', () => {
      const wrapper = shallow(<Profile/>);

     ...(other tests here)...
        it('clicks a button ', () => {

        wrapper.find('button').simulate('click');
        expect(onButtonClick.calledOnce).to.equal(true);
    })
});

komponennya adalah:

import _ from 'lodash';
import React, { Component, PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import ExpireAlert from '../components/alert';
import SocialAccount from '../components/socialAccounts'

const FIELDS = {
    name : {
        type : 'input',
        label : 'name'
    },
    username : {
        type : 'input',
        label: 'username'
    },
    email : {
        type : 'input',
        label: 'email'
    }
};

let alert = false;

export default class Profile extends Component {

    componentWillReceiveProps(nextProps){
      if(nextProps.userIsUpdated){
        alert = !alert
      }
    }

    handleSubmit(userData) { // update profile
     userData.id = this.props.userInfo.id
     this.props.updateUserInfo(userData)
    }

    renderField(fieldConfig, field) { // one helper per ea field declared
      const fieldHelper = this.props.fields[field];
      return (
        <label>{fieldConfig.label}
          <fieldConfig.type type="text" placeholder={fieldConfig.label} {...fieldHelper}/>
          {fieldHelper.touched && fieldHelper.error && <div>{fieldHelper.error}</div>}
        </label>
      );
    }

  render() {
    const {resetForm, handleSubmit, submitting, initialValues} = this.props;
      return (
        <div className="login">
          <div className="row">
            <div className="small-12 large-7 large-centered columns">
              <div className="component-wrapper">
                <ExpireAlert 
                    set={this.props.userIsUpdated}
                    reset={this.props.resetAlert}
                    status="success"
                    delay={3000}>
                    <strong> That was a splendid update! </strong>
                </ExpireAlert>
                <h3>Your Profile</h3>
                <SocialAccount
                  userInfo={this.props.userInfo}
                  unlinkSocialAcc={this.props.unlinkSocialAcc}
                />
                <form className="profile-form" onSubmit={this.props.handleSubmit(this.handleSubmit.bind(this))}>
                  <div className="row">
                    <div className="small-12 large-4 columns">
                      <img className="image" src={this.props.userInfo.img_url}/>
                    </div>
                    <div className="small-12 large-8 columns">
                      {_.map(FIELDS, this.renderField.bind(this))}
                    </div>
                    <div className="small-12 columns">
                      <button type="submit" className="primary button expanded" disabled={submitting}>
                        {submitting ? <i/> : <i/>} Update
                      </button>
                    </div>
                  </div>
                </form>
              </div>
            </div>
          </div>
      </div>
   );
  }
}

const validate = values => {
  const errors = {}
     if (!values.name) {
        errors.name = 'Name is Required'
      }
      if (!values.username) {
        errors.username = 'Username is Required'
      } else if (values.username.length > 30) {
        errors.username = 'Must be 30 characters or less'
      }
      if (!values.email) {
        errors.email = 'Email is Required'
      } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
        errors.email = 'Invalid email address'
      }
  return errors;
}

Profile.propTypes = {
  fields: PropTypes.object.isRequired,
  handleSubmit: PropTypes.func.isRequired,
  resetForm: PropTypes.func.isRequired,
  submitting: PropTypes.bool.isRequired
}

export default reduxForm({
  form: 'Profile',
  fields: _.keys(FIELDS),
  validate
})(Profile)

Setiap saran dihargai. Saya ingin membuat beberapa pengujian unit yang luar biasa!


person Christina Mitchell    schedule 13.05.2016    source sumber


Jawaban (2)


ZekeDroid benar. Cara lain untuk menangani situasi ini adalah dengan menggunakan mount, yang merupakan cara Enzim untuk merender komponen secara mendalam, artinya seluruh pohon komponen dirender. Ini akan dianggap sebagai pengujian integrasi, bukan pengujian unit. ZekeDroid menyebutkan "Ini mengasumsikan komponen Anda dapat menangani tidak menerima apa pun yang disediakan reduxForm, yang merupakan praktik yang baik karena Anda akan merasa lebih mudah untuk mengujinya.", dan saya yakin dia merujuk pada pengujian unit. Idealnya, pengujian Anda harus mencakup pengujian unit dan pengujian integrasi. Saya membuat proyek sederhana untuk menunjukkan cara melakukan pengujian unit dan integrasi dengan formulir redux. Lihat repo ini.

person Tyler Collier    schedule 26.05.2016

Anda memiliki dua export defaults di file Anda. Ini secara alami berarti bahwa yang kedua mengesampingkan yang pertama. Jika Anda benar-benar ingin dapat mengekspor versi reduxForm dan komponen itu sendiri, sebagaimana seharusnya untuk pengujian, hapus kata default dari komponen tersebut.

Pada pengujian Anda, Anda shallow merender, artinya tidak ada turunan yang dirender. Karena Anda mengimpor reduxForm, tidak banyak yang akan dirender, yang pasti bukan Profile Anda. Oleh karena itu, jika Anda menghapus kata kunci default seperti yang saya sebutkan, pengujian Anda akan mulai berfungsi saat Anda mengimpornya menggunakan impor bernama:

import { Profile } from 'path/to/component';

*Ini mengasumsikan komponen Anda dapat menangani tidak menerima apa pun yang reduxForm berikan, yang merupakan praktik yang baik karena Anda akan merasa lebih mudah untuk mengujinya.

person ZekeDroid    schedule 13.05.2016
comment
Saya mencoba menghapus default dari Profil kelas, serta formulir redux, dan menghasilkan beberapa kesalahan. ini salah satu cara menghapus ekspor dari kelas. - person Christina Mitchell; 13.05.2016
comment
hal. untuk memperjelas, saya mencoba menghapusnya satu per satu, tidak keduanya sekaligus. - person Christina Mitchell; 13.05.2016
comment
hapus default jelas bukan export. Saya sarankan membaca catatan sintaksis ES6 tentang cara mengimpor/mengekspor: 2ality. com/2014/09/es6-modules-final.html - person ZekeDroid; 15.05.2016