Vue单元测试-触发事件(表单提交)后数据未按预期更新

时间:2018-10-25 11:56:14

标签: unit-testing vue.js vue-test-utils

我正在测试表单数据在提交后发送。

ContactForm.spec.js

import Vue from "vue";
import Vuex from "vuex";
import { mount, shallowMount } from "@vue/test-utils";

import VeeValidate from "vee-validate";
import i18n from "@/locales";
import Vuetify from "vuetify";

import ContactForm from "@/components/Home/ContactForm.vue";

Vue.use(VeeValidate, { errorBagName: "errors" });
Vue.use(Vuetify);

Vue.use(Vuex);

describe("ContactForm.vue", () => {
  let store;
  let wrapper;
  let options;
  let input;

  const v = new VeeValidate.Validator();


  beforeEach(() => {
    const el = document.createElement("div");
    el.setAttribute("data-app", true);
    document.body.appendChild(el);
  });

  it("should sendMessage - valid form", async () => {
    // given
    store = new Vuex.Store({
      state: {
        language: "en",
        loading: false
      }
    })
    options = {
      sync: false,
      provide: {
        $validator () {
          return new VeeValidate.Validator();
        }
      },
      i18n,
      store
    };
    wrapper = mount(ContactForm, options);
    // when
    const radioInput = wrapper.findAll('input[type="radio"]');
    radioInput.at(1).setChecked(); // input element value is changed, v-model is not
    radioInput.at(1).trigger("change"); // v-model updated

    input = wrapper.find('input[name="givenName"]');
    input.element.value = "John"; // input element value is changed, v-model is not
    input.trigger("input"); // v-model updated

    input = wrapper.find('input[name="familyName"]');
    input.element.value = "Doe"; // input element value is changed, v-model is not
    input.trigger("input"); // v-model updated

    input = wrapper.find('input[name="email"]');
    input.element.value = "john.doe@example.com"; // input element value is changed, v-model is not
    input.trigger("input"); // v-model updated

    input = wrapper.find('textarea[name="messageContent"]');
    input.element.value = "Hello World!"; // input element value is changed, v-model is not
    input.trigger("input"); // v-model updated

    const contactForm = wrapper.find("form");
    contactForm.trigger("submit");
    await wrapper.vm.$nextTick();

    // then
    console.log("DATA: ", wrapper.vm.$data.contactLang);
    expect(wrapper.vm.validForm).toEqual(true);
  });

});

验证成功(因此在组件中将validForm设置为true) 但是测试未通过

console.log

    ✕ should sendMessage - valid form (476ms)

  ● ContactForm.vue › should sendMessage - valid form

    expect(received).toEqual(expected)

    Expected value to equal:
      true
    Received:
      false

组件Vue是

ContactForm.vue

<template>
  <form id="contactForm" @submit="sendMessage()">
    <input v-model="contactLang" type='hidden' data-vv-name="contactLang" v-validate="'required'" name='contactLang'>
    <v-layout row wrap align-center>
      <v-flex xs12 sm3 md3 lg3>
        <v-radio-group row :mandatory="false" v-model="gender" name="gender">
          <v-radio :label='genderLabel("f")' value="f" name="female"></v-radio>
          <v-radio :label='genderLabel("m")' value="m" name="male"></v-radio>
        </v-radio-group>
      </v-flex>
      <v-flex xs12 sm4 md4 lg4>
        <v-text-field
          v-model="givenName"
          browser-autocomplete="off"
          :label="$t('lang.views.home.contactForm.givenName')"
          data-vv-name="givenName"
          :error-messages="errors.collect('givenName')"
          v-validate="'required'"
          name="givenName">
        </v-text-field>
      </v-flex>
      <v-flex xs12 sm5 md5 lg5>
         <v-text-field
          v-model="familyName"
          browser-autocomplete="off"
          :label="$t('lang.views.home.contactForm.familyName')"
          data-vv-name="familyName"
          :error-messages="errors.collect('familyName')"
          v-validate="'required'"
          name="familyName">
        </v-text-field>
      </v-flex>
    </v-layout>
    <v-text-field
      browser-autocomplete="off"
      v-model="email"
      :label="$t('lang.views.home.contactForm.email')"
      data-vv-name="email"
      :error-messages="errors.collect('email')"
      v-validate="'required|email'"
      name="email">
    </v-text-field>
    <v-textarea v-model="messageContent" :label="$t('lang.views.home.contactForm.message')" :error-messages="errors.collect('messageContent')" :rules="[(v) => v.length <= 200 || 'Max 200 characters']" :counter="200" v-validate="'required'" data-vv-name="messageContent" name="messageContent"></v-textarea>
    <v-btn id="btnClear" round @click.native="clear">{{ $t('lang.views.global.clear') }}</v-btn>
    <v-btn round large color="primary" type="submit">{{ $t('lang.views.global.send') }}
      <v-icon right>email</v-icon><span slot="loader" class="custom-loader"><v-icon light>cached</v-icon></span>
    </v-btn>
  </form>
</template>

<script>
import swal from "sweetalert2";
import { mapState } from "vuex";
import appValidationDictionarySetup from "@/locales/appValidationDictionary";

export default {
  name: "contactForm",
  $_veeValidate: { validator: "new" },
  data() {
    return {
      contactLang: "",
      gender: "f",
      givenName: "",
      familyName: "",
      email: "",
      messageContent: "",
      validForm: false
    };
  },
   ...
  methods: {
   ...
    sendMessage: function() {
      console.log("sendMessage()...");
      this.$validator
        .validateAll()
        .then(isValid => {
          console.log("VALIDATION RESULT: ", isValid);
          this.validForm = isValid;
          if (!isValid) {
            console.log("VALIDATION ERROR");
            // console.log("Errors: ", this.$validator.errors.items.length);
            const alertTitle = this.$validator.dictionary.container[
              this.language
            ].custom.error;
            const textMsg = this.$validator.dictionary.container[this.language]
              .custom.correct_all;
            swal({
              title: alertTitle,
              text: textMsg,
              type: "error",
              confirmButtonText: "OK"
            });
            return;
          }
          console.log("validation success, form submitted validForm: ", this.validForm);
          return;
        })
        .catch(e => {
          // catch error from validateAll() promise
          console.log("error validation promise: ", e);
          this.validForm = false;
          return;
        });
    },
    clear: function() {
      this.contactLang = "";
      this.gender = "f";
      this.givenName = "";
      this.familyName = "";
      this.email = "";
      this.messageContent = "";
      this.validForm = false;
      this.$validator.reset();
    }
  },
  mounted() {
    appValidationDictionarySetup(this.$validator);
    this.$validator.localize(this.language);
    this.contactLang = this.language;
  }
};
</script>


</style>

和console.log调试是

 console.log src/components/Home/ContactForm.vue:90
    sendMessage()...

  console.log tests/unit/ContactForm.spec.js:242
    DATA:  en

  console.log src/components/Home/ContactForm.vue:94
    VALIDATION RESULT:  true

很奇怪的是,在console.log中,在验证结果之前显示的规范...中,DATA(contactLang)值是假的

      console.log src/components/Home/ContactForm.vue:90
        sendMessage()...

      console.log tests/unit/ContactForm.spec.js:242
        DATA:  en

      console.log src/components/Home/ContactForm.vue:94
        VALIDATION RESULT:  true

      console.log src/components/Home/ContactForm.vue:112
        validation success, form submitted validForm:  true

我猜这里有一个异步问题... timout?

感谢您的反馈

3 个答案:

答案 0 :(得分:1)

已解决 这实际上是一个超时问题

    contactForm.trigger("submit");
    await wrapper.vm.$nextTick();
    // then
    setTimeout(() => {
      expect(wrapper.vm.validForm).toEqual(true);
    }, 2000);

答案 1 :(得分:0)

我建议使用开玩笑的假计时器

mike

我建议先使测试失败以避免误报

有关开玩笑的假计时器的更多信息 https://jestjs.io/docs/en/timer-mocks.html

答案 2 :(得分:0)

我对组件的登录形式做了一个简单的测试,以防它对某人有所帮助

it("submit form call method login", () => {
    const login = jest.fn()
    const wrapper = shallowMount(Login, {
        methods: {
            login
        }
    })

    wrapper.findAll("v-form").at(0).trigger("submit")

    expect(login).toBeCalled()
})
相关问题