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

新手上路,请多包涵

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

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

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

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

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

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

按钮组件.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
})

原文由 J Dawg 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 696
2 个回答

方案一: 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的各种 mocking机制 来mock被测组件调用的抽象模块。

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

  1. click 处理程序的主体移动到共享的 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()
  })
})

演示

原文由 tony19 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果 jest.spyOn() 不起作用并且您需要存根来自第三方库的方法,您可以尝试创建一个可以在安装组件时注入的混合:

 const mock = jest.fn()

const wrapper = shallowMount(MyComponent, {
    localVue,
    store,
    router,
    mixins: [{
        methods: {
            $thirdPartyMethod: mock
        }
    }]
})

// later on in your test...
expect(mock).toHaveBeenCalled()

原文由 OzzyTheGiant 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题