javascript - Làm thế nào để giả lập Pinia và vue-i18n cùng lúc?
Tôi đang sử dụng Composition API của Vue 3 như sau:
cửa_hàng.ts
import { ref, Ref } from 'vue';
import { defineStore } from 'pinia';
export const useStore = defineStore('store', () => {
const isLoading: Ref<boolean> = ref(true);
function init() {
isLoading.value = true;
setTimeout(() => (isLoading.value = false), 3000);
}
return {
init,
isLoading,
};
});
Component.vue
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { useStore } from './store';
const { t } = useI18n();
const store = useStore();
const { isLoading } = storeToRefs(store);
</script>
<template>
<wb-button @click="store.init()">{{ t('init') }}</wb-button>
<p>Loading: {{ isLoading ? 'Yes' : 'No' }}</p>
</template>
Tất cả đều hoạt động ổn, nhưng khi tôi cố gắng thử nghiệm các thứ thì mọi thứ trở nên lộn xộn. Cho đến nay, tôi đã giả lập vue-i18n toàn cục như sau:
testSetup.ts
import { vi } from 'vitest';
vi.mock('vue-i18n', () => ({
useI18n: () => ({
t: (key: string) => key,
d: (key: string) => key,
}),
}));
Tuy nhiên, điều đó chỉ hiệu quả cho đến khi tôi cố gắng giả lập cửa hàng đồng thời như thế này:
Component.test.ts
import { it, expect, vi } from 'vitest';
import { mount} from '@vue/test-utils';
import { createTestingPinia } from '@pinia/testing';
import Component from './Component.vue';
const options = {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn(),
initialState: {
items: [],
},
}),
],
},
};
it('works', () => {
const wrapper = mount(Component, options);
expect(wrapper.findComponent(Component)).toBeTruthy();
});
Ngay khi tôi thêm thay đổi vào cấu hình plugin để kích hoạt giả lập Pinia, giả lập vue-i18n ngừng hoạt động và ném ra lỗi quen thuộc TypeError: $setup.t is not a function. Tôi cũng đã thử sử dụng config.global.mocks và config.global.plugins để giả lập vue-i18n nhưng tất cả các giải pháp này ngừng hoạt động ngay khi tôi thêm giả lập Pinia vào các bài kiểm tra. Tôi nghĩ lý do là đối tượng config.global bằng cách nào đó bị đặt lại bởi @pinia/testing nhưng tôi không biết cách ngăn chặn hành vi này.
package.json
"@pinia/testing": "0.0.16",
"@types/jsdom": "21.1.1",
"@types/node": "18.16.2",
"@vue/test-utils": "2.3.2",
"jsdom": "21.1.1"
"typescript": "~4.8.4",
"vite": "4.3.3",
"vitest": "0.30.1",
Câu trả lời #1
Vấn đề là thuộc tính createSpy mong đợi một hàm nhưng lại nhận được một lời gọi. Thay vi.fn() bằng vi.fn và mọi thứ sẽ hoạt động như mong đợi.
createTestingPinia({
createSpy: vi.fn, <--
...
})
Nguồn: https://pinia.vuejs.org/cookbook/testing.html#specifying-the-createspy-function
Câu trả lời #2
Lỗi cho biết component đã gắn không có hàm t, bởi vì bạn không thêm VueI18n như một plugin vào các tùy chọn gắn:
import { it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { createPinia } from 'pinia'
import Component from './Component.vue'
import { createI18n } from 'vue-i18n'
const options = {
global: {
plugins: [
createPinia(),
createI18n({
legacy: false,
locale: 'en-US',
messages: {
'en-US': {
init: 'Initialize'
}
}
})
]
}
}
it('does work', () => {
const wrapper = mount(Component, options)
expect(wrapper.findComponent(Component)).toBeTruthy()
})
Chi tiết hơn, global.plugins mong đợi một mảng plugin Vue (các đối tượng hoặc thể hiện lớp với một hàm install()) và làm cùng một việc như app.use(somePlugin).
Bạn cần đặt tại đó tất cả các plugin mà component được kiểm tra (và các component con, khi không mount nông) phụ thuộc vào. Điều này là vì mỗi component được kiểm tra một cách tách biệt (đó là lý do chúng được gọi là unit test), chúng không được mount bên trong ứng dụng thực tế.
Tóm lại, bạn đang thấy cùng một hành vi mà bạn sẽ thấy nếu bạn cố gắng sử dụng VueI18n mà không thêm .use(VueI18n) vào app.
Để giảm thiểu phần mã lặp lại trong các tệp kiểm thử, xuất một mountingOptions từ test.utils.ts:
import { createPinia } from 'pinia'
import { createI18n } from 'vue-i18n'
export const mountingOptions = {
global: {
plugins: [
createPinia(),
createI18n({
legacy: false,
locale: 'en-US',
messages: {
'en-US': {
init: 'Initialize'
}
}
})
]
}
}
Thông thường, mountingOptions nên chứa tất cả các plugin mà bạn đã thêm vào app, trong main.
Sau đó, trong bất kỳ bài kiểm tra nào:
import { mountingOptions } from '../test.utils'
//...
const wrapper = mount(Component, mountingOptions)
