【问题标题】:Mocking methods on a Vue instance during TDD在 TDD 期间模拟 Vue 实例上的方法
【发布时间】:2021-11-02 00:32:45
【问题描述】:

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

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

我也在使用 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
})

【问题讨论】:

    标签: vue.js vuejs2 tdd jestjs vue-test-utils


    【解决方案1】:

    正如 tony19 提到的,使用 spyOn 方法对你有用。我还发现我需要在模板中的方法中添加括号 () 才能将其拾取。我通过以下文件通过了测试:

    ButtonComponent.vue

    <template>
      <button @click="method()">Click me</button>
    </template>
    
    <script>
    export default {
      methods: {
        method() {
          // Have I been called?
        }
      }
    }
    </script>
    

    ButtonComponent.spec.js

    import ButtonComponent from '@/components/ButtonComponent'
    import { shallowMount } from '@vue/test-utils'
    
    it('will call the method when clicked', () => {
      const wrapper = shallowMount(ButtonComponent)
      const mockMethod = jest.spyOn(wrapper.vm, 'method')
    
      const button = wrapper.find('button')
      button.trigger('click')
    
      expect(mockMethod).toHaveBeenCalled()
      // passes
    })
    

    【讨论】:

    • 组件挂载时调用method()这样的模板方法不会直接调用吗?
    【解决方案2】:

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

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

    官方recommendation is to "abstract the hard parts away",并使用Jest的各种mocking mechanisms模拟被测组件调用的抽象模块。

    例如,要验证 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()
      })
    })
    

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

    使用setMethods() (deprecated as of 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()
      })
    })
    

    demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-29
      • 1970-01-01
      • 2014-05-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多