如何使用使用 promise 的方法对 vuejs 组件更新进行单元测试
Posted
技术标签:
【中文标题】如何使用使用 promise 的方法对 vuejs 组件更新进行单元测试【英文标题】:how to unittest a vuejs component update with methods using promise 【发布时间】:2017-12-16 16:29:16 【问题描述】:我正在用 Jest 测试一个 Vue 组件。这是我的工作目录:
这是我的组件Subscription.vue
:
<template>
<div id="page">
<page-title icon="fa-list-alt">
<translate slot="title">Subscription</translate>
<translate slot="comment">Consult the detail of your subscription</translate>
</page-title>
<panel v-if="error">
<span slot="title">
<icon icon="fa-exclamation-triangle"></icon>
<translate>Error</translate>
</span>
error
</panel>
<panel v-else-if="subscription_dict">
<span slot="title"> _subscription_address </span>
<div class="row" v-if="subscription_dict.product.hsi">
<div class="col-xs-12">
<subscription-hsi-panel
:hsi="subscription_dict.product.hsi"
:business="context.business">
</subscription-hsi-panel>
</div>
</div>
<div class="row" v-if="subscription_dict.product.itv">
<div class="col-xs-12">
<subscription-itv-panel
:itv="subscription_dict.product.itv">
</subscription-itv-panel>
</div>
</div>
<div class="row" v-if="subscription_dict.product.voip">
<div class="col-xs-6">
<panel icon="icon-voip">
<translate slot="title">Phone</translate>
telefon products
</panel>
</div>
</div>
</panel>
<div v-else class="text-center">
<i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i>
</div>
<span v-if="subscription_dict"><b>subscription_dict.product.voip : </b> subscription_dict.product.voip </br></span>
<span v-if="subscription_dict"><b>subscription_dict.product : </b> subscription_dict.product </br></span>
</div>
</template>
<script>
import PageTitle from '../../core/components/PageTitle.vue'
import SubscriptionHsiPanel from './SubscriptionHsiPanel.vue'
import SubscriptionItvPanel from './SubscriptionItvPanel.vue'
import Panel from '../../core/components/Panel.vue'
import Icon from '../../core/components/Icon.vue'
import api from '../../core/api.js'
export default
data()
return
subscription_dict: false,
error: false
,
props: ['requests_url', 'context'],
computed:
_subscription_address()
var sub_address = this.subscription_dict.subscription_address
return sub_address + ' - ' + this.subscription_dict.package.join(' - ')
,
created()
this.get_subscription()
this.translate()
,
methods:
get_subscription()
let self = this
api.get_subscription(self.requests_url.subscriptions_request)
.then(function(response)
self.subscription_dict = response
)
.catch(function(error)
if(error)
self.error = error
else
self.error = self.$gettext(
'We were not able to retrieve your subscription information!')
)
,
translate()
this.$gettext('Bridge hsi')
this.$gettext('Bridge voip_biz')
this.$gettext('Router')
,
components:
PageTitle,
Panel,
Icon,
SubscriptionHsiPanel,
SubscriptionItvPanel
</script>
<style lang="sass">
@import "../../core/css/tooltip"
.table
table-layout: fixed
> tbody > tr >
th
width: 33%
td
vertical-align: middle
</style>
这是我的测试subscription.js
:
import Vue from 'vue'
import translations from 'src/translations.json'
import GetTextPlugin from 'vue-gettext'
import VTooltip from 'v-tooltip'
import Subscription from 'src/subscription/components/Subscription'
jest.mock('../../core/api');
Vue.use(GetTextPlugin,
availableLanguages:
en: 'English',
fr: 'Français',
de: 'Deutsch',
,
defaultLanguage: 'fr',
translations: translations
)
Vue.use(VTooltip)
it('render when error', (done) =>
const renderer = require('vue-server-renderer').createRenderer()
const vm = new Vue(
el: document.createElement('div'),
render: h => h(Subscription,
props:
requests_url:
subscriptions_request: 'error_empty_promise'
)
)
renderer.renderToString(vm, (err, str) =>
setImmediate(() =>
expect(str).toMatchSnapshot()
done()
)
)
)
组件的方法get_subscription()
使用我的API函数get_subscription
:
import axios from 'axios'
export default
get_subscription(url)
return axios.get(url,
credentials: 'same-origin'
)
.then(function(response)
if(response.data.error)
return Promise.reject(response.data.error)
else
return Promise.resolve(response.data)
)
.catch(function(error)
return Promise.reject(false)
)
对于我的测试,我已经像这样模拟了这个函数:
const promises_object =
error_empty_promise: new Promise((resolve, reject) =>
process.nextTick(
() => reject(false)
)
)
export default
get_subscription(url)
return promises_object[url]
现在,在测试中,我渲染并与快照进行比较。我的问题是,在进行比较之前,我找不到等待get_subscription
的承诺是reject
的方法。
结果是我的快照反映了 DOM 更新之前组件的状态,这是在 API 的异步调用之后完成的。
有没有办法告诉 jest 等到 Promise
是 reject
后再调用 expect(str).toMatchSnapshot()
?
【问题讨论】:
facebook.github.io/jest/docs/tutorial-async.html 谢谢,这个例子我知道了。你能告诉我你如何在我的情况下应用它吗?在示例中,他直接在测试中调用异步函数,因此可以解析Promise
对象。这里我不能这样做,因为当组件状态为created
时调用该函数。
创建一个新的 Promise 并返回它。将其设置为在另一个 Promise 的 reject
子句中进行测试和解析。
对不起,我不明白。我已经从我的模拟 API 返回了 Promise
。我需要在哪里“创建一个新的 Promise 并返回它”。 ?感谢您迄今为止的帮助!
【参考方案1】:
Jest docs说
it
期望返回值是一个 Promise,它将是 解决了。
你有一个将被拒绝的 Promise,所以你需要一个在你的 Promise 被拒绝时解决的 Promise。
isRejected(rejectingPromise, someExpects)
return new Promise((resolve) =>
rejectingPromise.then(
() => null, // if it resolves, fail
(err) => // if it rejects, resolve
// Maybe do someExpects() here
resolve();
);
【讨论】:
我已尝试使用您的解决方案,但在get_subscription
Promise
被拒绝之前仍会创建快照。我仍然有使用微调器制作的快照,而不是错误消息。
也许将快照调用放在nextTick 调用中?
那行不通。 nextTick 调用只等到下一次 DOM 更新。在这里,我们必须等到 Promise 被解决/拒绝。这就是我卡住的地方。
如果你把快照测试放在我把“也许在这里做一些期望”的地方,它不应该在拒绝/解决之前发生。
我不明白,你上面粘贴的get_subscription(url)
的代码在src/core/__mocks__/api.js
中。这是模拟的 API。使用这个模拟 API 的组件测试在 src/__tests__/subscription/subscription.js
中。那么当it()
语句在subscription.js
中时,相关的expect()
语句怎么会在api.js
中呢?我一定错过了您的解决方案中的某些内容。以上是关于如何使用使用 promise 的方法对 vuejs 组件更新进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章