无法使用 Jest 中的提交按钮触发 Vuetify 表单提交

Posted

技术标签:

【中文标题】无法使用 Jest 中的提交按钮触发 Vuetify 表单提交【英文标题】:Fail to trigger Vuetify form submit using submit button in Jest 【发布时间】:2020-06-26 08:11:36 【问题描述】:

我正在尝试使用 Jest 和 Avoriaz 验证基于 Vuetify 组件的 Vue 表单的行为。 我可以在表单上触发submit.prevent,从而产生预期的行为。 但是在提交按钮上触发click 不起作用。

组件:

<template>
  <v-form
    ref="form"
    data-cy="form"
    @submit.prevent="login"
  >
    <v-text-field
      id="email"
      v-model="email"
      label="Email"
      name="email"
      prepend-icon="mdi-at"
      type="text"
      required
      autofocus
      data-cy="email-text"
    />
    <v-btn
      color="primary"
      type="submit"
      data-cy="login-btn"
    >
      Login
    </v-btn>
  </v-form>
</template>

<script>

export default 
  data () 
    return 
      email: 'test@test.com',
    
  ,
  computed: ,
  methods: 
    login: function () 
      console.log('Logging in')
    
  

</script>

测试设置:

import vuetify from '@/plugins/vuetify'
import  mount  from 'avoriaz'
import Form from '@/views/Form'

describe('Form', () => 
  const mountFunction = options => 
    return mount(Form, 
      vuetify,
      ...options
    )
  

Vue 和 Vuetify 设置在 @/plugins/vuetify 中完成:

import Vue from 'vue'
import Vuetify from 'vuetify/lib'

Vue.use(Vuetify)

export default new Vuetify(
)

以下测试成功(因此模拟工作):

  it('can trigger form directly', () => 
    const login = jest.fn()
    const wrapper = mountFunction()
    wrapper.setData( 'email': 'test@com' )
    wrapper.setMethods( login )

    let element = wrapper.first('[data-cy=form]')
    element.trigger('submit.prevent')

    expect(login).toHaveBeenCalledTimes(1)
  )

但实际测试提交按钮,失败:

  it('can trigger form through button', () => 
    const login = jest.fn()
    const wrapper = mountFunction()
    wrapper.setData( 'email': 'test@test.com' )
    wrapper.setMethods( login )

    const button = wrapper.first('[type=submit]')
    button.trigger('click')

    expect(login).toHaveBeenCalledTimes(1)
  )

更新: 也许package.json中的一些相关依赖:


  ..
  "dependencies": 
    "axios": "^0.19.1",
    "core-js": "^3.4.4",
    "vue": "^2.6.11",
    "vue-router": "^3.1.3",
    "vuetify": "^2.1.0",
    "vuex": "^3.1.2"
  ,
  "devDependencies": 
    ..
    "avoriaz": "^6.3.0",
    "vue-jest": "^3.0.5",
    "vuetify-loader": "^1.3.0"
  

更新: 使用 test-utils 时,非 Vuetify 组件(&lt;form&gt;&lt;btn),以下测试成功:

const localVue = createLocalVue()

localVue.use(Vuetify)
describe('Form', () => 
  const mountFunction = options => 
    return shallowMount(Form, 
      localVue,
      vuetify,
      ...options
    )
  
  it('can trigger form through button alternative', async () => 
    const login = jest.fn()
    const wrapper = mountFunction( attachToDocument: true )
    try 
      wrapper.setData( 'email': 'test@test.com' )
      wrapper.setMethods( login )

      const button = wrapper.find('[type=submit]')
      expect(button).toBeDefined()
      button.trigger('click')
      await Vue.nextTick()

      expect(login).toHaveBeenCalledTimes(1)
     finally 
      wrapper.destroy()
    
  )
)

然后切换到 Vuetify 组件会导致测试失败。

【问题讨论】:

【参考方案1】:

显然,当使用 Vuetify 组件(&lt;v-form&gt;&lt;v-btn&gt;)时,需要一些关键要素才能使其工作:

Vue test-utils 而不是 avoriaz mount 而不是 shallowMount 包括attachToDocument,之后也需要清理 (wrapper.destroy()) 正如@Husam Ibrahim 也提到的,需要asynchronously await Vue.nextTick() 要让 setData 充分发挥作用,您可能需要额外的await Vue.nextTick()。在我的完整代码中,我有表单验证(在输入上使用:rules,在表单上使用v-model,并且按钮的:disabled 绑定到v-model 数据元素。这需要两个nextTick()s

以下工作('@/plugins/vuetify' 与问题中的相同:

import vuetify from '@/plugins/vuetify'
import Vue from 'vue'
import  createLocalVue, mount  from '@vue/test-utils'
import Form from '@/views/Form'
import Vuetify from 'vuetify/lib'

const localVue = createLocalVue()

localVue.use(Vuetify)

describe('Form', () => 
  const mountFunction = options => 
    return mount(Form, 
      localVue,
      vuetify,
      ...options
    )
  
  it('can trigger form through button alternative', async () => 
    const login = jest.fn()
    const wrapper = mountFunction( attachToDocument: true )
    try 
      wrapper.setData( 'email': 'test@test.com' )
      await Vue.nextTick() # might require multiple
      wrapper.setMethods( login )

      const button = wrapper.find('[type=submit]')
      button.trigger('click')
      await Vue.nextTick()

      expect(login).toHaveBeenCalledTimes(1)
     finally 
      wrapper.destroy()
    
  )
)

【讨论】:

很高兴您找到了解决方案 :) 对我来说它只使用mount而不是shallowMount就可以工作【参考方案2】:

这可能是一个异步问题。我会尝试在触发点击事件之后和断言之前等待Vue.nextTick() ..

  it('can trigger form through button', async () => 
    const login = jest.fn()
    const wrapper = mountFunction()
    wrapper.setData( 'email': 'test@test.com' )
    wrapper.setMethods( login )

    const button = wrapper.first('[type=submit]')
    button.trigger('click')

    await Vue.nextTick()
    expect(login).toHaveBeenCalledTimes(1)
  )

【讨论】:

我必须在 ` () => ` 之前添加 async 以允许使用 await。但仍然只观察到 0 次登录调用。 会不会有一些不确定的行为。我更改了测试运行它,观察到 1 个呼叫。然后清理,没有让测试通过。恢复了,又失败了。我的 IntelliJ 本地历史显示测试成功,使用您的代码,但现在失败了 抱歉,我忘记了 async 关键字。如果涉及任何非确定性行为,我会感到惊讶。你的代码和测试在我看来是完全确定的。 谢谢,但很遗憾无法让测试成功。

以上是关于无法使用 Jest 中的提交按钮触发 Vuetify 表单提交的主要内容,如果未能解决你的问题,请参考以下文章

Jest Vue,测试触发点击按钮,预期 1,收到:0

AngularJS:表单内的所有按钮触发提交?

无法使用 nuxt 和 jest 模拟服务

使用 vue-test-utils / jest 触发 Quasar QBtn 点击

在同一个按钮按下时触发 iframe 并提交表单

在不使用提交按钮的情况下触发标准 HTML5 验证(表单)?