如何使用 Mocha 在 Vue.js 中测试 DOM 更新?
How to test DOM update in Vue.js with Mocha?
我正在努力理解 Vue.js 使用 Karma、Mocha 和 Chai 进行单元测试的一些基本概念。
这是我的组件:
VueExample.vue
<template>
<div>
<p>{{ name }}</p>
<input v-model="name">
</div>
</template>
<script>
export default {
name: 'VueExample',
data () {
return {
name: 'Bruce Lee'
};
}
}
</script>
这就是我尝试测试它的方式:
VueExample.spec.js
import Vue from 'vue';
import VueExample from "../../../components/VueExample";
describe('VueExample.vue', () => {
let vm;
beforeEach(() => {
const Constructor = Vue.extend(VueExample);
vm = new Constructor().$mount();
});
it('should change the name', done => {
const input = vm.$el.querySelector('input');
input.value = 'Chuck Norris';
expect(vm.$el.querySelector('p').textContent).to.equal('Bruce Lee');
Vue.nextTick(() =>{
expect(vm.$el.querySelector('p').textContent).to.equal('Chuck Norris');
console.log(vm.$el.querySelector('p').textContent);
done();
});
});
});
我使用 Karma 运行 测试和 Chai 作为断言库。 karma.conf.js
中的一切都已正确配置。当我 运行 这个测试时,它失败了。标签 <p>
内的文本不会改变。 console.log
命令输出李小龙.
测试 运行 使用 Firefox。
更改输入的值不会触发 Vue 更新模型(因为输入的属性不是反应性的)。'
您可以在浏览器中尝试。 运行 在控制台中 document.getElementsByTagName('input')[0].value = 'Chuck Norris'
并没有任何反应,p
元素的文本仍然是 "Bruce Lee".
Vue的触发方式是input
事件。所以你应该发送一个 input
事件。这可能是这样的:
let event = document.createEvent('HTMLEvents')
event.initEvent('input', true, true)
vm.$el.querySelector('input').dispatchEvent(event)
v-model
relies on the input event,仅通过编辑 JavaScript 中的输入值不会发生这种情况。在 Vue 之外也是如此,如下所示:
function logInput(e) {
console.log('input', e.target.value)
}
function update() {
const input = document.querySelector('#input1')
input.value = "foo"
}
<input oninput="logInput(event)" id="input1">
<button onclick="update()">Update textbox</button>
<div>Clicking button changes text but does not emit <code>input</code> event. See console.</div>
在您的测试中手动调度输入事件应该可行。在您的示例中,在设置 input.value
:
之后执行 input.dispatchEvent(new Event('input'))
const input = vm.$el.querySelector('input');
input.value = 'Chuck Norris';
input.dispatchEvent(new Event('input')); // input event required to update v-model
@Anatoly 在评论之一中建议使用 Vue Test Utils。我在玩弄并想出了一个我想分享的解决方案:
yarn add -D @vue/test-utils
或:
npm install --save-dev @vue/test-utils
然后测试文件看起来像这样:
import Vue from 'vue';
import { shallowMount } from '@vue/test-utils';
import VueExample from "../../../components/VueExample";
describe('VueExample.vue', () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(VueExample);
});
it('should change the name', done => {
const textInput = wrapper.find('input');
textInput.setValue('Chuck Norris');
textInput.trigger('input');
expect(wrapper.find('p').text()).to.equal('Bruce Lee');
Vue.nextTick(() => {
expect(wrapper.find('p').text()).to.equal('Chuck Norris');
done();
});
});
});
此示例中只有一个测试,但我仍然使用 beforeEach
以使其更容易通过进一步测试进行扩展。这里我们挂载Vue组件VueExample
。在测试中我们找到 <input>
标签,将其值设置为 Chuck Norris 并触发 input
事件。我们可以看到<p>
标签的文本节点没有变化,仍然是李小龙。这是由于 Vue 中 DOM 更新的异步性质。
使用nextTick()
我们可以检查chane已经生效并且<p>
标签的文本节点等于之前设置的值,即Chuck Norris.
我正在努力理解 Vue.js 使用 Karma、Mocha 和 Chai 进行单元测试的一些基本概念。
这是我的组件:
VueExample.vue
<template>
<div>
<p>{{ name }}</p>
<input v-model="name">
</div>
</template>
<script>
export default {
name: 'VueExample',
data () {
return {
name: 'Bruce Lee'
};
}
}
</script>
这就是我尝试测试它的方式:
VueExample.spec.js
import Vue from 'vue';
import VueExample from "../../../components/VueExample";
describe('VueExample.vue', () => {
let vm;
beforeEach(() => {
const Constructor = Vue.extend(VueExample);
vm = new Constructor().$mount();
});
it('should change the name', done => {
const input = vm.$el.querySelector('input');
input.value = 'Chuck Norris';
expect(vm.$el.querySelector('p').textContent).to.equal('Bruce Lee');
Vue.nextTick(() =>{
expect(vm.$el.querySelector('p').textContent).to.equal('Chuck Norris');
console.log(vm.$el.querySelector('p').textContent);
done();
});
});
});
我使用 Karma 运行 测试和 Chai 作为断言库。 karma.conf.js
中的一切都已正确配置。当我 运行 这个测试时,它失败了。标签 <p>
内的文本不会改变。 console.log
命令输出李小龙.
测试 运行 使用 Firefox。
更改输入的值不会触发 Vue 更新模型(因为输入的属性不是反应性的)。'
您可以在浏览器中尝试。 运行 在控制台中 document.getElementsByTagName('input')[0].value = 'Chuck Norris'
并没有任何反应,p
元素的文本仍然是 "Bruce Lee".
Vue的触发方式是input
事件。所以你应该发送一个 input
事件。这可能是这样的:
let event = document.createEvent('HTMLEvents')
event.initEvent('input', true, true)
vm.$el.querySelector('input').dispatchEvent(event)
v-model
relies on the input event,仅通过编辑 JavaScript 中的输入值不会发生这种情况。在 Vue 之外也是如此,如下所示:
function logInput(e) {
console.log('input', e.target.value)
}
function update() {
const input = document.querySelector('#input1')
input.value = "foo"
}
<input oninput="logInput(event)" id="input1">
<button onclick="update()">Update textbox</button>
<div>Clicking button changes text but does not emit <code>input</code> event. See console.</div>
在您的测试中手动调度输入事件应该可行。在您的示例中,在设置 input.value
:
input.dispatchEvent(new Event('input'))
const input = vm.$el.querySelector('input');
input.value = 'Chuck Norris';
input.dispatchEvent(new Event('input')); // input event required to update v-model
@Anatoly 在评论之一中建议使用 Vue Test Utils。我在玩弄并想出了一个我想分享的解决方案:
yarn add -D @vue/test-utils
或:
npm install --save-dev @vue/test-utils
然后测试文件看起来像这样:
import Vue from 'vue';
import { shallowMount } from '@vue/test-utils';
import VueExample from "../../../components/VueExample";
describe('VueExample.vue', () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(VueExample);
});
it('should change the name', done => {
const textInput = wrapper.find('input');
textInput.setValue('Chuck Norris');
textInput.trigger('input');
expect(wrapper.find('p').text()).to.equal('Bruce Lee');
Vue.nextTick(() => {
expect(wrapper.find('p').text()).to.equal('Chuck Norris');
done();
});
});
});
此示例中只有一个测试,但我仍然使用 beforeEach
以使其更容易通过进一步测试进行扩展。这里我们挂载Vue组件VueExample
。在测试中我们找到 <input>
标签,将其值设置为 Chuck Norris 并触发 input
事件。我们可以看到<p>
标签的文本节点没有变化,仍然是李小龙。这是由于 Vue 中 DOM 更新的异步性质。
使用nextTick()
我们可以检查chane已经生效并且<p>
标签的文本节点等于之前设置的值,即Chuck Norris.