TDD期间Vue实例上的模拟方法

J D*_*awg 5 tdd vue.js jestjs vuejs2 vue-test-utils

在构建Vue应用程序时,我正在学习TDD,并尝试遵守严格的法律,即仅编写足够的生产代码来满足失败的单元测试。我确实很喜欢这种方法,但是在向Vue实例添加方法并测试事件是否从模板中的元素触发时调用方法方面遇到了障碍。

我无法找到关于如何模拟Vue方法的任何建议,因为如果我模拟代理方法,最终将不会被调用(我正在使用Jest和Vue Test Utils)。

我也在使用赛普拉斯(Cypress),因此我可以在e2e中填写此测试,但我希望能够涵盖更多的单元测试。

我拥有Edd Yerburgh的书“ Testing Vuejs Applications”,但在有关测试组件方法的部分中,他只是简单地指出以下内容:

“通常,组件在内部使用方法。例如,单击按钮时登录到控制台........您可以将它们视为私有方法,它们不打算在组件外部使用。私有方法是实现细节,因此您不能直接为它们编写测试。”

这种方法显然不允许遵循更严格的TDD法则,那么TDD纳粹如何处理呢?

谢谢你的时间。

// ButtonComponent.vue

<template>
    <button @click="method">Click me</button>
</template>

<script>
    export default: {
        methods: {
            method () {
                // Have I been called?
            }
        }
    }
</script>
Run Code Online (Sandbox Code Playgroud)

这是我的测试,如果有帮助的话:

// ButtonComponent.spec.js

it('will call the method when clicked, () => {

    const wrapper = shallowMount(ButtonComponent)

    const mockMethod = jest.fn()

    wrapper.vm.method = mockMethod

    const button = wrapper.find('button')

    button.vm.$emit('click')

    expect(mockMethod).toHaveBeenCalled()
    // Expected mock function to have been called, but it was not called
})
Run Code Online (Sandbox Code Playgroud)

ton*_*y19 31

解决方案1: jest.spyOn(Component.methods, 'METHOD_NAME')

您可以jest.spyOn在安装之前使用模拟组件方法:

import MyComponent from '@/components/MyComponent.vue'

describe('MyComponent', () => {
  it('click does something', async () => {
    const mockMethod = jest.spyOn(MyComponent.methods, 'doSomething')
    await shallowMount(MyComponent).find('button').trigger('click')
    expect(mockMethod).toHaveBeenCalled()
  })
})
Run Code Online (Sandbox Code Playgroud)

解决方案 2:将方法移动到可以模拟的单独文件中

官方的建议是“把难的部分抽象出来”,使用Jest的各种mocking机制来mock被测组件调用的抽象模块。

例如,要验证click调用了-handler:

  1. click-handler 的主体移动到共享的 JavaScript 文件中。
  2. 将共享模块导入被测组件和您的测试中(确保在两种情况下使用相同的导入路径)。
  3. 调用jest.mock()以模拟共享模块的导出函数。
  4. 重置测试套件中的模拟beforeEach()。这可能仅在套件中有多个测试时才有必要。
// @/components/MyComponent/utils.js
export function doSomething() { /*...*/ } //1??

// @/components/MyComponent/MyComponent.vue (<script>)
import { doSomething } from '@/components/MyComponent/utils' //2??

export default {
  methods: {
    onClick() {
      doSomething() //1??
    }
  }
}

// @/test/MyComponent.spec.js
import { doSomething } from '@/components/MyComponent/utils' //2??
jest.mock('@/components/MyComponent/utils') //3??

describe('MyComponent', () => {
  beforeEach(() => doSomething.mockClear()) //4??

  it('click does something', async () => {
    await shallowMount(MyComponent).find('button').trigger('click')
    expect(doSomething).toHaveBeenCalled()
  })
})
Run Code Online (Sandbox Code Playgroud)

解决方案 3:setMethods()(v1.0 之前)

使用setMethods()自 v1.0 起已弃用)覆盖组件方法:

describe('MyComponent', () => {
  it('click does something', async () => {
    // Option A:
    const mockMethod = jest.fn()
    const wrapper = shallowMount(MyComponent)
    wrapper.setMethods({ doSomething: mockMethod })
    await wrapper.find('button').trigger('click')
    expect(mockMethod).toHaveBeenCalled()

    // Option B:
    const mockMethod = jest.fn()
    const wrapper = shallowMount(MyComponent, {
      methods: {
        doSomething: mockMethod
      }
    })
    await wrapper.find('button').trigger('click')
    expect(mockMethod).toHaveBeenCalled()
  })
})
Run Code Online (Sandbox Code Playgroud)

演示