测试 Vuetify (Vue.js) - 第二次调用 mount 抛出错误
Testing Vuetify (Vue.js) - Second call on mount throws error
我目前在测试我的 Vue 应用程序时遇到了一个行为(特别是当包含 vuetify 时)。我正在使用 Jest 作为测试运行器,但在使用 mocha 时遇到了相同的行为。
首先要注意的是,问题只发生在@vue/test-utils 的挂载上,而不是shallowMount。此外,它仅在您使用两次 mount 时发生(我猜原因是 Vue 对象的污染,但稍后会更多)。
现在我的组件只是一个基本 v-data-table 的包装器,属性 值绑定到它的项目和一些用于复选框而不是文本的自定义插槽。
现在是问题。首先,这是我的测试的第一个变体的样子(这基本上是 vuetify 推荐的方式。看看 here。由于测试本身并不重要,我只希望在这里为真
import Vue from 'vue';
import Vuetify from 'vuetify';
import { mount, createLocalVue, shallowMount } from '@vue/test-utils';
import PermissionTable from '@/components/PermissionTable.vue';
import { expect } from 'chai';
const localVue = createLocalVue();
// Vue.use is not in the example but leaving it will cause the error that
// the data table is not registered
Vue.use(Vuetify);
describe('Permissiontable.vue', () => {
let vuetify;
let tableItems;
beforeEach(() => {
tableItems = [];
vuetify = new Vuetify();
});
it('will test the first thing',() => {
const wrapper = mount(PermissionTable, {
localVue,
vuetify,
propsData: {
value: tableItems
}
});
expect(true).to.be(true);
});
it('will test the second thing',() => {
const wrapper = mount(PermissionTable, {
localVue,
vuetify,
propsData: {
value: tableItems
}
});
expect(true).to.be(true);
});
});
现在,正如已经在不使用 Vue.use(Vuetify) 的情况下评论的那样,我将收到组件 v-data-table 未注册的错误。有了它,我留下了以下行为
- 按预期测试第一件事运行并成功
- 第二件事失败出现以下错误
Type Error: Cannot read property '$scopedSlots' of undefined
并在挂载 (....) 时失败。为了使行为更奇怪,如果我调试并停在这一行,
运行 在调试控制台中手动挂载它在第一次时也失败并出现相同的错误。
如果我 运行 它再次起作用。例如,如果我执行此测试 4 次,就会发生这种情况。第一个会成功 -> 第二个会失败 -> 第三个和第四个会再次成功。
现在我确信,如果函数获得相同的输入,它们的行为方式是相同的。因此必须在第一次调用时更改安装的输入。我的猜测是 Vue class 以某种方式被污染了。所以如果我查看 localVue 的文档,这个实用程序是为了防止全局 Vue class 的污染。所以我将代码更改为
import Vue from 'vue';
import Vuetify from 'vuetify';
import { mount, createLocalVue, shallowMount } from '@vue/test-utils';
import PermissionTable from '@/components/PermissionTable.vue';
import { expect } from 'chai';
describe('Permissiontable.vue', () => {
let vuetify;
let tableItems;
let localVue;
beforeEach(() => {
tableItems = [];
localVue = createLocalVue();
vuetify = new Vuetify();
localVue.use(vuetify);
});
it('will test the first thing',() => {
const wrapper = mount(PermissionTable, {
localVue,
vuetify,
propsData: {
value: tableItems
}
});
expect(true).to.be(true);
});
it('will test the second thing',() => {
const wrapper = mount(PermissionTable, {
localVue,
vuetify,
propsData: {
value: tableItems
}
});
expect(true).to.be(true);
});
});
所以我为每个测试创建了一个新的 localVue 实例和 vuetify。并让 localVue 使用 vuetify。现在这让我回到错误
[Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.
我还尝试了注入 vuetify(实例化)或 Vuetify 的各种改变。使用全局 Vue.use 等。最后我总是留下这两种行为之一。
现在的解决方法似乎是将每个测试都写在一个文件中,但我认为这是非常糟糕的做法,我想了解这里到底发生了什么。
我现在还为 Vuetify 和 Vue-Test-Utils 创建了 Issues,因为我无法想象这是预期的行为,并且制作了我的组件的精简版本,并将测试作为 Repo
rm-test-mount-fails-on-second-exec
重现:
- 克隆回购
- npm 安装
- npm 运行 test:unit
版本:
Vue:2.6.10
Vue-Test-Utils: 1.0.0-beta.29
验证: 2.1.12
在您的第一段代码中唯一看起来不合适的地方是您正在编写 Vue.use(Vuetify)
并且在执行挂载时还使用了 Vuetify 实例。
我建议您保留 Vue.use(Vuetify)
并像这样安装您的组件:
const wrapper = mount(PermissionTable, {
localVue, // vuetify is removed from this object
propsData: {
value: tableItems
}
});
附带说明一下,单元测试通常应该使用 shallowMount
。我不知道你的用例,但如果可能,请使用它而不是 mount
坏消息
目前这确实是 vue-test-utils 中的一个错误。在我打开问题后,我发现了另一个有问题的问题
与我的相似,我很确定根本原因与我的情况相同。
显然,这是由于 v.beta.29
中的 vue 实用程序发生了变化
可以在此处找到问题 #1130
好消息
有一种解决方法可以让您的测试再次运行,直到解决此错误。您需要使用 sync: false 选项进行挂载,因此在顶部示例中的挂载看起来像
const wrapper = mount(PermissionTable, {
localVue,
vuetify,
propsData: {
value: tableItems
},
sync: false
});
我仍然认为这是一个严重的错误,因为无论设置如何,每次 运行 相同的测试都应该以相同的方式运行。一旦有消息表明此问题已得到解决,我将立即更新此 post。
我目前在测试我的 Vue 应用程序时遇到了一个行为(特别是当包含 vuetify 时)。我正在使用 Jest 作为测试运行器,但在使用 mocha 时遇到了相同的行为。
首先要注意的是,问题只发生在@vue/test-utils 的挂载上,而不是shallowMount。此外,它仅在您使用两次 mount 时发生(我猜原因是 Vue 对象的污染,但稍后会更多)。
现在我的组件只是一个基本 v-data-table 的包装器,属性 值绑定到它的项目和一些用于复选框而不是文本的自定义插槽。
现在是问题。首先,这是我的测试的第一个变体的样子(这基本上是 vuetify 推荐的方式。看看 here。由于测试本身并不重要,我只希望在这里为真
import Vue from 'vue';
import Vuetify from 'vuetify';
import { mount, createLocalVue, shallowMount } from '@vue/test-utils';
import PermissionTable from '@/components/PermissionTable.vue';
import { expect } from 'chai';
const localVue = createLocalVue();
// Vue.use is not in the example but leaving it will cause the error that
// the data table is not registered
Vue.use(Vuetify);
describe('Permissiontable.vue', () => {
let vuetify;
let tableItems;
beforeEach(() => {
tableItems = [];
vuetify = new Vuetify();
});
it('will test the first thing',() => {
const wrapper = mount(PermissionTable, {
localVue,
vuetify,
propsData: {
value: tableItems
}
});
expect(true).to.be(true);
});
it('will test the second thing',() => {
const wrapper = mount(PermissionTable, {
localVue,
vuetify,
propsData: {
value: tableItems
}
});
expect(true).to.be(true);
});
});
现在,正如已经在不使用 Vue.use(Vuetify) 的情况下评论的那样,我将收到组件 v-data-table 未注册的错误。有了它,我留下了以下行为
- 按预期测试第一件事运行并成功
- 第二件事失败出现以下错误
Type Error: Cannot read property '$scopedSlots' of undefined
并在挂载 (....) 时失败。为了使行为更奇怪,如果我调试并停在这一行, 运行 在调试控制台中手动挂载它在第一次时也失败并出现相同的错误。 如果我 运行 它再次起作用。例如,如果我执行此测试 4 次,就会发生这种情况。第一个会成功 -> 第二个会失败 -> 第三个和第四个会再次成功。
现在我确信,如果函数获得相同的输入,它们的行为方式是相同的。因此必须在第一次调用时更改安装的输入。我的猜测是 Vue class 以某种方式被污染了。所以如果我查看 localVue 的文档,这个实用程序是为了防止全局 Vue class 的污染。所以我将代码更改为
import Vue from 'vue';
import Vuetify from 'vuetify';
import { mount, createLocalVue, shallowMount } from '@vue/test-utils';
import PermissionTable from '@/components/PermissionTable.vue';
import { expect } from 'chai';
describe('Permissiontable.vue', () => {
let vuetify;
let tableItems;
let localVue;
beforeEach(() => {
tableItems = [];
localVue = createLocalVue();
vuetify = new Vuetify();
localVue.use(vuetify);
});
it('will test the first thing',() => {
const wrapper = mount(PermissionTable, {
localVue,
vuetify,
propsData: {
value: tableItems
}
});
expect(true).to.be(true);
});
it('will test the second thing',() => {
const wrapper = mount(PermissionTable, {
localVue,
vuetify,
propsData: {
value: tableItems
}
});
expect(true).to.be(true);
});
});
所以我为每个测试创建了一个新的 localVue 实例和 vuetify。并让 localVue 使用 vuetify。现在这让我回到错误
[Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.
我还尝试了注入 vuetify(实例化)或 Vuetify 的各种改变。使用全局 Vue.use 等。最后我总是留下这两种行为之一。
现在的解决方法似乎是将每个测试都写在一个文件中,但我认为这是非常糟糕的做法,我想了解这里到底发生了什么。
我现在还为 Vuetify 和 Vue-Test-Utils 创建了 Issues,因为我无法想象这是预期的行为,并且制作了我的组件的精简版本,并将测试作为 Repo
rm-test-mount-fails-on-second-exec
重现:
- 克隆回购
- npm 安装
- npm 运行 test:unit
版本:
Vue:2.6.10
Vue-Test-Utils: 1.0.0-beta.29
验证: 2.1.12
在您的第一段代码中唯一看起来不合适的地方是您正在编写 Vue.use(Vuetify)
并且在执行挂载时还使用了 Vuetify 实例。
我建议您保留 Vue.use(Vuetify)
并像这样安装您的组件:
const wrapper = mount(PermissionTable, {
localVue, // vuetify is removed from this object
propsData: {
value: tableItems
}
});
附带说明一下,单元测试通常应该使用 shallowMount
。我不知道你的用例,但如果可能,请使用它而不是 mount
坏消息
目前这确实是 vue-test-utils 中的一个错误。在我打开问题后,我发现了另一个有问题的问题 与我的相似,我很确定根本原因与我的情况相同。 显然,这是由于 v.beta.29
中的 vue 实用程序发生了变化可以在此处找到问题 #1130
好消息
有一种解决方法可以让您的测试再次运行,直到解决此错误。您需要使用 sync: false 选项进行挂载,因此在顶部示例中的挂载看起来像
const wrapper = mount(PermissionTable, {
localVue,
vuetify,
propsData: {
value: tableItems
},
sync: false
});
我仍然认为这是一个严重的错误,因为无论设置如何,每次 运行 相同的测试都应该以相同的方式运行。一旦有消息表明此问题已得到解决,我将立即更新此 post。