使用 Jest 对 Vuetify 数据表进行单元测试
Posted
技术标签:
【中文标题】使用 Jest 对 Vuetify 数据表进行单元测试【英文标题】:Unit testing a Vuetify Data Table with Jest 【发布时间】:2020-08-12 12:29:28 【问题描述】:我已经使用 Vuetify 创建了一个 Vue.js 应用程序,现在我正在尝试对包含 Vuetify 数据表的组件进行单元测试。数据表是使用 Axios 从后端 REST API 填充的,这在我运行应用程序时工作正常,但是在我的单元测试中(我用 Jest 模拟 Axios),永远不会填充数据表
这是我的组件的来源
<template>
<v-container fluid>
<v-card>
<v-card-title>
Results
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:headers="headers"
:items="results"
:search="search"
:loading="loading"
loading-text="Loading results..."
:custom-sort="customSort"
>
<template v-slot:item.startTime="item">formatDate(item.startTime)</template>
<template v-slot:item.endTime="item">formatDate(item.endTime)</template>
</v-data-table>
</v-card>
</v-container>
</template>
<script>
import axios from 'axios';
import moment from 'moment';
function dateArrayToMoment(date)
return moment()
.year(date[0])
.month(date[1])
.date(date[2])
.hour(date[3])
.minute(date[4]);
export default
name: 'ResultsList',
data()
return
loading: true,
search: '',
headers: [
text: 'Task', align: 'start', sortable: false, value: 'title' ,
text: 'State', value: 'state' ,
text: 'Start Time', value: 'startTime' ,
text: 'End Time', value: 'endTime' ,
text: 'Result', value: 'resultMessage'
],
results: []
;
,
mounted()
this.loadResults();
,
methods:
async loadResults()
try
let response = await axios.get('BACKEND_SERVER/results', );
this.results = response.data;
this.loading = false;
// eslint-disable-next-line no-debugger
catch (error)
// eslint-disable-next-line no-console
console.error(error);
// you can handle different errors differently
// or just display an error message instead of your table using a <v-if> tag
// or navigate to an error page
,
formatDate(date)
return dateArrayToMoment(date).format('YYYY-MM-DD hh:mm');
,
customSort(items, index, isDesc)
items.sort((a, b) =>
if (index[0] == 'startTime' || index[0] == 'endTime')
if (!isDesc[0])
return (
dateArrayToMoment(b[index]).toDate() -
dateArrayToMoment(a[index]).toDate()
);
else
return (
dateArrayToMoment(a[index]).toDate() -
dateArrayToMoment(b[index]).toDate()
);
);
return items;
;
</script>
这是测试组件的测试规范
import Vue from 'vue'
import Vuetify from 'vuetify'
import ResultsList from '@/components/ResultsList'
import mount, createLocalVue from '@vue/test-utils'
import axios from 'axios'
import flushPromises from 'flush-promises';
Vue.use(Vuetify)
const localVue = createLocalVue()
var results = [
'id': 1,
'state': 'COMPLETED',
'startTime': [2020, 4, 21, 19, 42],
'endTime': [2020, 4, 21, 19, 42],
'type': 'Example Scheduled Task',
'title': 'Example Scheduled Task at 2020-04-21 19:42:00',
'resultMessage': 'Task finished successfully'
,
'id': 2,
'state': 'COMPLETED',
'startTime': [2020, 4, 22, 13, 36],
'endTime': [2020, 4, 22, 13, 36],
'type': 'Example Scheduled Task',
'title': 'Example Scheduled Task at 2020-04-22 13:36:00',
'resultMessage': 'Task finished successfully'
,
'id': 3,
'state': 'COMPLETED',
'startTime': [2020, 4, 22, 13, 37],
'endTime': [2020, 4, 22, 13, 37],
'type': 'Example Scheduled Task',
'title': 'Example Scheduled Task at 2020-04-22 13:37:00',
'resultMessage': 'Task finished successfully'
];
// Use Jest to mock the Axios
jest.mock('axios');
describe('ResultsList.vue', () =>
let vuetify
beforeEach(() =>
vuetify = new Vuetify()
axios.get.mockResolvedValue(results);
)
it('should have a custom title and match snapshot', async () =>
const wrapper = mount(ResultsList,
localVue,
vuetify,
propsData:
title: 'Foobar',
,
)
await flushPromises()
// With jest we can create snapshot files of the html output
expect(wrapper.html()).toMatchSnapshot()
)
)
如您所见,我使用 Jest 模拟 Axios,因此它返回一些测试数据并使用 Jest 验证快照。
问题是快照不包含任何数据(测试或其他),尽管调用了 flushPromises 以确保在拍摄快照之前解决所有承诺。
这是快照。如您所见,数据表、测试或其他方式中没有显示任何数据。
// Jest Snapshot v1
exports[`ResultsList.vue should match snapshot 1`] = `
<div class="container container--fluid" title="Foobar">
<div class="v-card v-sheet theme--light">
<div class="v-card__title">
Results
<div class="spacer"></div>
<div class="v-input v-input--hide-details theme--light v-text-field v-text-field--single-line">
<div class="v-input__control">
<div class="v-input__slot">
<div class="v-text-field__slot"><label for="input-4" class="v-label theme--light" style="left: 0px; position: absolute;">Search</label><input id="input-4" type="text"></div>
<div class="v-input__append-inner">
<div class="v-input__icon v-input__icon--append"><i aria-hidden="true" class="v-icon notranslate mdi mdi-magnify theme--light"></i></div>
</div>
</div>
</div>
</div>
</div>
<div class="v-data-table theme--light">
<div class="v-data-table__wrapper">
<table>
<colgroup>
<col class="">
<col class="">
<col class="">
<col class="">
<col class="">
</colgroup>
<thead class="v-data-table-header">
<tr>
<th role="columnheader" scope="col" aria-label="Task" class="text-start"><span>Task</span></th>
<th role="columnheader" scope="col" aria-label="State: Not sorted. Activate to sort ascending." aria-sort="none" class="text-start sortable"><span>State</span><i aria-hidden="true" class="v-icon notranslate v-data-table-header__icon mdi mdi-arrow-up theme--light" style="font-size: 18px;"></i></th>
<th role="columnheader" scope="col" aria-label="Start Time: Not sorted. Activate to sort ascending." aria-sort="none" class="text-start sortable"><span>Start Time</span><i aria-hidden="true" class="v-icon notranslate v-data-table-header__icon mdi mdi-arrow-up theme--light" style="font-size: 18px;"></i></th>
<th role="columnheader" scope="col" aria-label="End Time: Not sorted. Activate to sort ascending." aria-sort="none" class="text-start sortable"><span>End Time</span><i aria-hidden="true" class="v-icon notranslate v-data-table-header__icon mdi mdi-arrow-up theme--light" style="font-size: 18px;"></i></th>
<th role="columnheader" scope="col" aria-label="Result: Not sorted. Activate to sort ascending." aria-sort="none" class="text-start sortable"><span>Result</span><i aria-hidden="true" class="v-icon notranslate v-data-table-header__icon mdi mdi-arrow-up theme--light" style="font-size: 18px;"></i></th>
</tr>
</thead>
<tbody>
<tr class="v-data-table__empty-wrapper">
<td colspan="5">No data available</td>
</tr>
</tbody>
</table>
</div>
<div class="v-data-footer">
<div class="v-data-footer__select">Rows per page:<div class="v-input v-input--hide-details v-input--is-label-active v-input--is-dirty theme--light v-text-field v-select">
<div class="v-input__control">
<div role="button" aria-haspopup="listbox" aria-expanded="false" aria-owns="list-17" class="v-input__slot">
<div class="v-select__slot">
<div class="v-select__selections">
<div class="v-select__selection v-select__selection--comma">10</div><input aria-label="$vuetify.dataTable.itemsPerPageText" id="input-17" readonly="readonly" type="text" aria-readonly="false" autocomplete="off">
</div>
<div class="v-input__append-inner">
<div class="v-input__icon v-input__icon--append"><i aria-hidden="true" class="v-icon notranslate mdi mdi-menu-down theme--light"></i></div>
</div><input type="hidden" value="10">
</div>
<div class="v-menu">
<!---->
</div>
</div>
</div>
</div>
</div>
<div class="v-data-footer__pagination">–</div>
<div class="v-data-footer__icons-before"><button type="button" disabled="disabled" class="v-btn v-btn--disabled v-btn--flat v-btn--icon v-btn--round v-btn--text theme--light v-size--default" aria-label="Previous page"><span class="v-btn__content"><i aria-hidden="true" class="v-icon notranslate mdi mdi-chevron-left theme--light"></i></span></button></div>
<div class="v-data-footer__icons-after"><button type="button" disabled="disabled" class="v-btn v-btn--disabled v-btn--flat v-btn--icon v-btn--round v-btn--text theme--light v-size--default" aria-label="Next page"><span class="v-btn__content"><i aria-hidden="true" class="v-icon notranslate mdi mdi-chevron-right theme--light"></i></span></button></div>
</div>
</div>
</div>
</div>
`;
【问题讨论】:
我很好奇你在这里想出了什么。我的下意识的回答是我想知道当你模拟 Axios 时它是否只返回模拟并忽略你的数据。 另一个建议是,我会将您的代码分成更有用的模块。取出您的 axios HTTP 调用并将其放入该特定 API 的自己的服务中,并使用此组件仅控制数据表。这样在测试时您可以使用嵌入式数据,而不必依赖 Axios。 Axios 是一个外部引用,是的,如果失败,它会阻止您的数据表被填充,但这是 Axios 的问题,而不是您的数据表。 【参考方案1】:一些想法:
拍摄快照前请致电wrapper.vm.$nextTick()
。
不要使用Vue.use()
,试试localVue.use(Vuetify)
【讨论】:
已经尝试过了,但恐怕没有什么不同以上是关于使用 Jest 对 Vuetify 数据表进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章
在 Nuxt、Vue、jest 和 Vuetify 中为项目编写单元测试
单元测试包含带有插槽的 Vuetify 数据表的 Vue 组件
Vue/Typescript/Jest - Jest 单元测试语法错误:意外的令牌导入