Vue.js 单元测试 - 带有异步数据的模拟服务

Mar*_*gar 5 unit-testing vue.js

项目: https: //github.com/marioedgar/webpack-unit-test

我有一个使用 vue CLI 生成的 Vue.js 应用程序。我只稍微编辑了 HelloWorld 组件以从我的测试服务中获取一些异步数据,您可以在这里看到:

<template>
  <h1>{{ message }}</h1>
</template>

<script>
  import service from './test.service'
  export default {
    name: 'HelloWorld',
    created () {
      service.getMessage().then(message => {
        this.message = message
      })
    },
    data () {
      return {
        message: 'A'
      }
    }
  }
</script>
<style scoped>
</style>
Run Code Online (Sandbox Code Playgroud)

测试服务位于同一目录中,非常简单:

class Service {
  getMessage () {
    return new Promise((resolve, reject) => {
      console.log('hello from test service')
      resolve('B')
    })
  }
}

const service = new Service()
export default service
Run Code Online (Sandbox Code Playgroud)

因此,为了模拟此服务,我使用 webpack vue-loader 来注入模拟服务,如官方文档中所述:

https://vue-loader.vuejs.org/en/workflow/testing-with-mocks.html

这是我的测试,与示例几乎相同:

import Vue from 'vue'
const HelloInjector = require('!!vue-loader?inject!../../../src/components/HelloWorld')

const Hello = HelloInjector({
  // mock it
  './test.service': {
    getMessage () {
      return new Promise((resolve, reject) => {
        resolve('C')
      })
    }
  }
})

describe('HelloWorld.vue', () => {
  it('should render', () => {
    const vm = new Vue({
      template: '<div><test></test></div>',
      components: {
        'test': Hello
      }
    }).$mount()
    expect(vm.$el.querySelector('h1').textContent).to.equal('C')
  })
})
Run Code Online (Sandbox Code Playgroud)

我面临两个问题:

  1. 测试失败,因为断言是在模拟的 Promise 解决之前执行的。据我了解,这是因为当我进行断言时,vue 生命周期尚未完全完成。等待下一个周期的常见模式是将我的断言包装在下一个刻度函数周围,如下所示:

  it('should render', (done) => {
    const vm = new Vue({
      template: '<div><test></test></div>',
      components: {
        'test': Hello
      }
    }).$mount()
    Vue.nextTick(() => {
      expect(vm.$el.querySelector('h1').textContent).to.equal('C')
      done()
    })
  })
Run Code Online (Sandbox Code Playgroud)

然而,除非我嵌套 3 个 nextTicks,否则这不起作用,这对我来说似乎非常老套。为了让它发挥作用,我缺少什么吗?这个例子看起来非常简单,但是如果没有大量的 nextTicks 我就无法让这个测试通过

  1. 我不断地间歇性地收到奇怪的错误...此警告可能会在 50% 的时间内出现,而且根本不一致。

[vue warn] 组件挂载失败:模板或渲染函数未定义

同样,这种情况只是有时发生。我可以运行完全相同的单元测试而不进行任何更改,并且它会在 50% 的时间内向我显示此消息。

Lui*_*duz 3

我真的不明白为什么有时组件无法安装。我什至不确定它与注射器有关,但无论如何,我通过不使用它来保持测试的一致性;尝试不同的方法。

如果通过注入服务props而不是直接使用服务,则该组件可能更具可测试性。

<template>
  <div>
  <h1>{{ message }}</h1>
</div>
</template>

<script>
  import service from './test.service'

  export default {
    name: 'HelloWorld',
    created () {
      this.service.getMessage().then(message => {
        this.message = message
      })
    },
    data () {
      return {
        message: 'A'
      }
    },
    props: {
      service: {
        default: service
      }
    }
  }
</script>
<style scoped>
</style>
Run Code Online (Sandbox Code Playgroud)

这使得注入器变得不必要,因为可以将模拟服务传递给propsData构造函数中使用的组件。

import Vue from 'vue'

import Async from '@/components/Async'

const service = {
  getMessage () {
    return new Promise((resolve, reject) => {
      resolve('C')
    })
  }
}

describe('Async.vue', () => {
  let vm
  before(() => {
    const Constructor = Vue.extend(Async)
    vm = new Constructor({
      propsData: {
        service: service
      }
    }).$mount()
  })
  it('should render', function () {
    // Wrapping the tick inside a promise, bypassing PhantomJS's lack of support
    return (new Promise(resolve => Vue.nextTick(() => resolve()))).then(() => {
      expect(vm.$el.querySelector('h1').textContent).to.equal('C')
    })
  })
})
Run Code Online (Sandbox Code Playgroud)