如何在 $route 上对 VueJS 观察器进行单元测试
How to unit test VueJS watcher on $route
我正在测试一个使用 vue router 来监视 $route 的单文件组件。问题是我无法同时更改路线和触发观察者功能的测试。
测试文件:
import { createLocalVue, shallow } from 'vue-test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
const localVue = createLocalVue();
localVue.use(Vuex);
const $route = {
path: '/my/path',
query: { uuid: 'abc' },
}
wrapper = shallow({
localVue,
store,
mocks: {
$route,
}
});
it('should call action when route changes', () => {
// ensure jest has a clean state for this mocked func
expect(actions['myVuexAction']).not.toHaveBeenCalled();
vm.$set($route.query, 'uuid', 'def');
//vm.$router.replace(/my/path?uuid=def') // tried when installing actual router
//vm.$route.query.uuid = 'def'; // tried
//vm.$route = { query: { uuid: 'def'} }; // tried
expect(actions['myVuexAction']).toHaveBeenLastCalledWith({ key: true });
});
我在SFC中的watch方法:
watch: {
$route() {
this.myVuexAction({ key: true });
},
},
您如何模拟路由器,以便您可以观察它并测试观察方法是否按预期工作?
实际做到这一点的方法是使用 vue-test-utils 包装器方法,setData
。
wrapper.setData({ $route: { query: { uuid: 'def'} } });
这就是我测试路由更改的方式,它将当前路由名称作为 css class 添加到我的应用程序组件:
import VueRouter from 'vue-router'
import { shallowMount, createLocalVue } from '@vue/test-utils'
import MyApp from './MyApp'
describe('MyApp', () => {
it('adds current route name to css classes on route change', () => {
// arrange
const localVue = createLocalVue()
localVue.use(VueRouter)
const router = new VueRouter({ routes: [{path: '/my-new-route', name: 'my-new-route'}] })
const wrapper = shallowMount(MyApp, { localVue, router })
// act
router.push({ name: 'my-new-route' })
// assert
expect(wrapper.find('.my-app').classes()).toContain('my-new-route')
})
})
使用 vue@2.6.11
和 vue-router@3.1.3
进行了测试。
我检查了 VueRouter
如何初始化 $route
和 $router
并在我的测试中复制了它。以下工作无需直接使用 VueRouter
:
const localVue = createLocalVue();
// Mock $route
const $routeWrapper = {
$route: null,
};
localVue.util.defineReactive($routeWrapper, '$route', {
params: {
step,
},
});
Object.defineProperty(localVue.prototype, '$route', {
get() { return $routeWrapper.$route; },
});
// Mock $router
const $routerPushStub = sinon.stub();
localVue.prototype.$router = { push: $routerPushStub };
const wrapper = shallowMount(TestComponent, {
localVue,
});
更新 $route
应该始终通过替换整个对象来完成,这是在 $route
上不使用深度观察者的唯一方式,也是 VueRouter
的行为方式:
$routeWrapper.$route = { params: { step: 1 } };
await vm.wrapper.$nextTick();
来源:install.js
对我有用
let $route = {
name: 'any-route',
};
我们定义了一个 $route 并且我们调用了 like
wrapper = mount(YourComponent, {
mocks: {
$route,
},
});
我的组件是这样的
@Watch('$route', { deep: true, immediate: true, })
async onRouteChange(val: Route) {
if (val.name === 'my-route') {
await this.getDocumentByUrl();
await this.allDocuments();
}
};
pd:我使用打字稿,但这适用于另一种格式
最后是我的测试
it('my test', ()=>{
const getDocumentByUrl = jest.spyOn(wrapper.vm, 'getDocumentByUrl');
const allDocuments = jest.spyOn(wrapper.vm, 'allDocuments');
wrapper.vm.$route.name = 'my-route';
await flushPromises();
expect(getDocumentByUrl).toHaveBeenCalled();
expect(allDocuments).toHaveBeenCalled();
})
我正在测试一个使用 vue router 来监视 $route 的单文件组件。问题是我无法同时更改路线和触发观察者功能的测试。
测试文件:
import { createLocalVue, shallow } from 'vue-test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
const localVue = createLocalVue();
localVue.use(Vuex);
const $route = {
path: '/my/path',
query: { uuid: 'abc' },
}
wrapper = shallow({
localVue,
store,
mocks: {
$route,
}
});
it('should call action when route changes', () => {
// ensure jest has a clean state for this mocked func
expect(actions['myVuexAction']).not.toHaveBeenCalled();
vm.$set($route.query, 'uuid', 'def');
//vm.$router.replace(/my/path?uuid=def') // tried when installing actual router
//vm.$route.query.uuid = 'def'; // tried
//vm.$route = { query: { uuid: 'def'} }; // tried
expect(actions['myVuexAction']).toHaveBeenLastCalledWith({ key: true });
});
我在SFC中的watch方法:
watch: {
$route() {
this.myVuexAction({ key: true });
},
},
您如何模拟路由器,以便您可以观察它并测试观察方法是否按预期工作?
实际做到这一点的方法是使用 vue-test-utils 包装器方法,setData
。
wrapper.setData({ $route: { query: { uuid: 'def'} } });
这就是我测试路由更改的方式,它将当前路由名称作为 css class 添加到我的应用程序组件:
import VueRouter from 'vue-router'
import { shallowMount, createLocalVue } from '@vue/test-utils'
import MyApp from './MyApp'
describe('MyApp', () => {
it('adds current route name to css classes on route change', () => {
// arrange
const localVue = createLocalVue()
localVue.use(VueRouter)
const router = new VueRouter({ routes: [{path: '/my-new-route', name: 'my-new-route'}] })
const wrapper = shallowMount(MyApp, { localVue, router })
// act
router.push({ name: 'my-new-route' })
// assert
expect(wrapper.find('.my-app').classes()).toContain('my-new-route')
})
})
使用 vue@2.6.11
和 vue-router@3.1.3
进行了测试。
我检查了 VueRouter
如何初始化 $route
和 $router
并在我的测试中复制了它。以下工作无需直接使用 VueRouter
:
const localVue = createLocalVue();
// Mock $route
const $routeWrapper = {
$route: null,
};
localVue.util.defineReactive($routeWrapper, '$route', {
params: {
step,
},
});
Object.defineProperty(localVue.prototype, '$route', {
get() { return $routeWrapper.$route; },
});
// Mock $router
const $routerPushStub = sinon.stub();
localVue.prototype.$router = { push: $routerPushStub };
const wrapper = shallowMount(TestComponent, {
localVue,
});
更新 $route
应该始终通过替换整个对象来完成,这是在 $route
上不使用深度观察者的唯一方式,也是 VueRouter
的行为方式:
$routeWrapper.$route = { params: { step: 1 } };
await vm.wrapper.$nextTick();
来源:install.js
对我有用
let $route = {
name: 'any-route',
};
我们定义了一个 $route 并且我们调用了 like
wrapper = mount(YourComponent, {
mocks: {
$route,
},
});
我的组件是这样的
@Watch('$route', { deep: true, immediate: true, })
async onRouteChange(val: Route) {
if (val.name === 'my-route') {
await this.getDocumentByUrl();
await this.allDocuments();
}
};
pd:我使用打字稿,但这适用于另一种格式
最后是我的测试
it('my test', ()=>{
const getDocumentByUrl = jest.spyOn(wrapper.vm, 'getDocumentByUrl');
const allDocuments = jest.spyOn(wrapper.vm, 'allDocuments');
wrapper.vm.$route.name = 'my-route';
await flushPromises();
expect(getDocumentByUrl).toHaveBeenCalled();
expect(allDocuments).toHaveBeenCalled();
})