TDD期间Vue实例上的模拟方法 [英] Mocking methods on a Vue instance during TDD

查看:78
本文介绍了TDD期间Vue实例上的模拟方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

我拥有《测试Vue.js应用程序》这本书,由Edd Yerburgh撰写,但在有关测试组件方法的部分中,他仅声明了以下内容:

通常,组件在内部使用方法.例如,单击按钮时登录到控制台,您可以将其视为私有方法-不应在组件外部使用它们.unesdoc.unesco.org unesdoc.unesco.org私有方法是实现的详细信息,因此您不必直接为它们编写测试.

这种方法显然不允许遵循更严格的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
})
 

解决方案

解决方案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:将方法移到可以模拟的单独文件中

官方建议"; ,并使用Jest的各种模拟机制 >模拟被测组件调用的抽象模块.

例如,要验证是否调用了click处理程序:

  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()(低于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()
  })
})
 

演示

I'm learning TDD whilst building my Vue app, and trying to abide by the strict laws of only writing enough production code to satisfy a failing unit test. I am really enjoying this approach, but I have run into a roadblock in regards to adding methods to a Vue instance, and testing that they have been called when the event fires from the element in the template.

I cannot find any suggestions as to how I can mock a Vue method given that if I mock the proxied method, it ends up not being called (I'm using Jest and Vue Test Utils).

I am also using Cypress, so I can fill in this test in e2e, but I would like to be able to cover as much as possible with unit tests.

I own the book "Testing Vue.js Applications" by Edd Yerburgh, but in the section regarding testing component methods, he simply states the following:

Often, components use methods internally. For example, to log to the console when a button is clicked [...] You can think of these as private methods—they aren’t intended to be used outside of the component. Private methods are implementation details, so you don’t write tests for them directly.

This approach obviously does not allow the stricter laws of TDD to be followed, so how do the TDD purists handle this?

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

解决方案

Solution 1: jest.spyOn(Component.methods, 'METHOD_NAME')

You could use jest.spyOn to mock the component method before mounting:

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

Solution 2: Move methods into separate file that could be mocked

The official recommendation is to "abstract the hard parts away", and use Jest's various mocking mechanisms to mock the abstracted module invoked by the component under test.

For example, to verify a click-handler is invoked:

  1. Move the click-handler's body into a shared JavaScript file.
  2. Import the shared module into the component under test and in your tests (make sure to use the same import path in both cases).
  3. Call jest.mock() to mock the exported functions of the shared module.
  4. Reset the mock in your test suite's beforeEach(). This might only be necessary when there are multiple tests in the suite.

// @/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()
  })
})

Solution 3: setMethods() (pre v1.0)

Use setMethods() (deprecated as of v1.0) to overwrite a component method:

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

这篇关于TDD期间Vue实例上的模拟方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆