问题:在 TDD 期间在 Vue 实例上模拟方法

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

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

我也在使用 Cypress,所以我可以在 e2e 中填写这个测试,但我希望能够尽可能多地覆盖单元测试。

我拥有 Edd Yerburgh 的《测试 Vue.js 应用程序》一书,但在有关测试组件方法的部分中,他简单地陈述了以下内容:

通常,组件在内部使用方法。例如,在单击按钮时登录到控制台 [...] 您可以将这些视为私有方法 - 它们不打算在组件之外使用。私有方法是实现细节,所以你不要直接为它们编写测试。

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

ButtonComponent.vue

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

<script>
    export default: {
        methods: {
            method () {
                // Have I been called?
            }
        }
    }
</script>

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

解答

解决方案一: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()
  })
})

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

官方的建议是“抽象掉困难的部分”,并使用 Jest 的各种模拟机制模拟被测组件调用的抽象模块。

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

  1. click-handler 的主体移动到一个共享的 JavaScript 文件中。

  2. 将共享模块导入到被测组件和您的测试中(确保在两种情况下使用相同的导入路径)。

3.调用jest.mock()模拟共享模块的导出函数。

  1. 重置测试套件中的模拟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()
  })
})

解决方案3:setMethods()(pre 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()
  })
})

演示

Logo

前往低代码交流专区

更多推荐