Vue,vuex:如何使用命名空间存储和模拟对组件进行单元测试?
Posted
技术标签:
【中文标题】Vue,vuex:如何使用命名空间存储和模拟对组件进行单元测试?【英文标题】:Vue, vuex: how to unit test a component with namespaced store and mocking? 【发布时间】:2020-08-09 09:03:44 【问题描述】:我正在使用 vue、vuex 和 vuetify 构建登录组件。我决定在商店中使用命名空间的身份验证模块,这导致了我的问题。
我正在使用 TDD 来解决这个问题。我的 e2e 测试有效。但是我编写了这个单元测试(使用 mockStore),它应该只验证一个正确的动作已经被调度:
describe('Login component', () =>
let wrapper
const mockStore =
dispatch: jest.fn(),
beforeEach(() =>
wrapper = mount(Login,
localVue,
mocks: $store: mockStore ,
computed:
error: () => 'test error',
,
data: () => (
valid: true
)
)
)
it('should dispatch login action', async () =>
wrapper.find('[data-test="username"]').setValue('username')
wrapper.find('[data-test="password"]').setValue('password')
await wrapper.vm.$nextTick()
await wrapper.vm.$nextTick()
wrapper.find('[data-test="login"]').trigger('click')
await wrapper.vm.$nextTick()
expect(mockStore.dispatch).toHaveBeenCalledWith(
`auth/$LOGIN`,
username: 'username', password: 'password'
)
)
)
组件仅通过以下方式使用mapActions
:
...mapActions('auth', [LOGIN])
以及触发它的按钮:
<v-btn
color="info"
@click="login( username, password )"
data-test="login"
:disabled="!valid"
>Login</v-btn>
我得到的错误是:
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'auth/' of undefined"
如果我将命名空间放在mapActions
中,那么我得到的调度操作名称没有命名空间(duh)并且测试失败:
- Expected
+ Received
- "auth/login",
+ "login",
我实际上可以通过如下映射操作来修复它:
...mapActions( login: `auth/$LOGIN` )
但我真的更喜欢使用命名空间版本,因为当我有更多操作时它会变得丑陋。
我正在研究 vuex 源代码,但在尝试访问 _modulesNamespaceMap
时它失败了,但它对我来说太复杂了。
测试这个的最佳方法是什么?我现在应该放弃嘲笑并使用真正的商店吗?
完整的项目可用here 并且与此问题相关的提交是4a7e749d4
【问题讨论】:
【参考方案1】:对于迁移到 Vue 3 Test Utils 的任何人,请注意上述答案所依赖的 createLocalVue
方法已在 @vue/test-utils 中删除(请参阅 https://next.vue-test-utils.vuejs.org/migration/#no-more-createlocalvue)。
相反,它建议使用来自 Vuex 的 createStore
方法。我能够让命名空间模块像这样工作:
/* ... other imports and setup ... */
import mount from "@vue/test-utils";
import Logon from "path/to/your/logon/component";
import createStore from "vuex";
describe('Login component', () =>
const actions =
login: jest.fn(),
;
const mockStore = createStore(
modules:
auth:
namespaced: true,
actions,
,
,
);
let wrapper;
beforeEach(() =>
wrapper = mount(Login,
global:
plugins: [mockStore],
,
);
);
it('should dispatch login action', async () =>
/*...test code goes here */
)
)
【讨论】:
【参考方案2】:以the example on the vue-test-utils
docs 为基础,我认为这应该可行:
/* ... other imports and setup ... */
import Vuex from 'vuex'
describe('Login component', () =>
let wrapper
const actions =
login: jest.fn(),
const mockStore = new Vuex(
modules:
auth:
namespaced: true,
actions,
,
,
)
beforeEach(() =>
wrapper = mount(Login,
localVue,
mocks: $store: mockStore ,
computed:
error: () => 'test error',
,
data: () => (
valid: true
)
)
)
it('should dispatch login action', async () =>
wrapper.find('[data-test="username"]').setValue('username')
wrapper.find('[data-test="password"]').setValue('password')
await wrapper.vm.$nextTick()
await wrapper.vm.$nextTick()
wrapper.find('[data-test="login"]').trigger('click')
await wrapper.vm.$nextTick()
expect(actions.login).toHaveBeenCalled() // <-- pretty sure this will work
expect(actions.login).toHaveBeenCalledWith( // <-- not as sure about this one
username: 'username',
password: 'password',
)
)
)
【讨论】:
太棒了。我最终使用了类似的方法,唯一困扰我的是创建一个实际的 Vuex 对象以上是关于Vue,vuex:如何使用命名空间存储和模拟对组件进行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章
打字稿:引用 Vuex 存储模块会导致 VueJs 2.5 的命名空间错误