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

时间:2017-10-19 23:16:37

标签: 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>
&#13;
&#13;
&#13;

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

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

const service = new Service()
export default service
&#13;
&#13;
&#13;

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

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

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

&#13;
&#13;
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')
  })
})
&#13;
&#13;
&#13;

我面临两个问题:

  1. 测试失败,因为在解析模拟的promise之前执行了断言。根据我的理解,这是因为当我做断言时,vue生命周期还没有完全完成。等待下一个周期的常见模式是将我的断言包含在下一个tick函数中,如下所示:
  2. &#13;
    &#13;
      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()
        })
      })
    &#13;
    &#13;
    &#13;

    然而,这不起作用,除非我嵌套3次nextTicks,这对我来说似乎非常讨厌。有什么我想让这个工作吗?这个例子看起来非常简单,但是如果没有很多nextTicks,我就无法通过这个测试

    1. 我不停地得到一个奇怪的错误,间歇性地......这个警告可能有50%的时间出现,并且完全不一致。
    2. [vue warn]无法安装组件:未定义模板或渲染功能

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

1 个答案:

答案 0 :(得分:2)

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

如果通过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>

这使得注入器变得不必要,因为可以使用构造函数中的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')
    })
  })
})