提问人:dunct 提问时间:11/14/2023 最后编辑:dunct 更新时间:11/20/2023 访问量:74
Vue、Vitest 和 MSW:模拟和测试 GraphQL 查询导致无法组合
Vue, Vitest and MSW: Mocking and testing a GraphQL query result in composable not possible
问:
编辑
我做了一个 stackblitz 示例,其中我拥有最多的问题。你可以在这里找到它:https://stackblitz.com/edit/vitejs-vite-axxjsu?file=src%2Fcomposables%2FuseFetchCountries.spec.ts
编辑结束
我做了一个简单的虚拟应用程序,因为最终它将适合一个更大的企业应用程序,但我首先做了一个概念验证以确保它有效......它还没有!我不明白为什么会这样。
我已经按照 https://mswjs.io/ 上的文档在测试运行程序中获取模拟。因此,我有一个文件,用于设置带有处理程序的服务器。我有一个 for Vitest,它在 setupFiles 属性中说明,并在 MSW 文档中说明。我在运行应用程序时在控制台中看到请求被模拟,因为它确实拦截了它。但是,它在测试运行程序中不执行任何操作。src/mocks/server.ts
src/setup-file.ts
vitest.config.ts
beforeAll
afterEach
afterAll
我有一个简单的可组合项,可以从公共 graphql 端点获取国家/地区(仅用于测试:-))。
// /src/composables/useFetchCountries.ts
const GET_COUNTRIES = gql`
query GetCountries {
countries {
code
name
currency
}
}
`;
const useFetchCountries = () => {
const { result } = useQuery<GetCountriesQuery>(GET_COUNTRIES);
const countries = computed(() => result.value?.countries);
return {
countries,
};
};
然后在 App.vue 中检索它。
<template>
<h1>Some countries</h1>
<ul>
<li v-for="country in countries" :key="country.code">
{{ country.name }} | {{ country.code }}
</li>
</ul>
</template>
<script setup lang="ts">
import useFetchCountries from "./composables/useFetchCountries";
const { countries } = useFetchCountries();
</script>
然后我对可组合项的测试如下(:
// /src/composables/useFetchCountries.spec.ts
import { provideApolloClient } from "@vue/apollo-composable";
import { createMockClient } from "mock-apollo-client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import useFetchCountries from "./useFetchCountries";
let mockApolloClient = createMockClient();
describe.only("composable - useFetchCountries", () => {
beforeEach(() => {
mockApolloClient = createMockClient();
provideApolloClient(mockApolloClient);
});
afterEach(() => {
vi.restoreAllMocks();
});
it("retrieves all a couple countries", async () => {
const { countries } = useFetchCountries();
const dummy = [
{ code: "AD", name: "Andorra", currency: "EUR" },
{ code: "AT", name: "Austria", currency: "EUR" },
{ code: "AX", name: "Åland", currency: "EUR" },
{ code: "BE", name: "Belgium", currency: "EUR" },
{ code: "BL", name: "Saint Barthélemy", currency: "EUR" },
];
expect(countries.value).toBe(dummy);
});
});
好了,现在终于有了我在处理程序中拦截 GetCountries 查询并返回模拟的内容。
// /src/mocks/handlers.ts
import { HttpResponse, graphql } from "msw";
const coupleCountries = new Map([
["AD", { code: "AD", name: "Andorra", currency: "EUR" }],
["AT", { code: "AT", name: "Austria", currency: "EUR" }],
["AX", { code: "AX", name: "Åland", currency: "EUR" }],
["BE", { code: "BE", name: "Belgium", currency: "EUR" }],
["BL", { code: "BL", name: "Saint Barthélemy", currency: "EUR" }],
]);
export const handlers = [
graphql.query("GetCountries", ({ query }) => {
console.log('Intercepted "GetCountries" query: ', query);
return HttpResponse.json({
data: {
countries: Array.from(coupleCountries.values()),
},
});
}),
];
但是,由于 Vue 计算,在运行测试时,国家/地区已经未定义!我不知道如何解决这个问题,因为我不久前才开始使用 Vue。我明白了它的主要要点,但是通过编写像这样的更高级的测试,我完全被蒙在鼓里。
在较大的应用程序中,可组合项中的某些方法对从 useQuery 检索到的数据执行操作。我想测试这些方法,以确保它们是正确的,并且以后不会改变,从而产生副作用。如何修复上面的测试以确保模拟数据实际上在测试中的任何想法?
答:
你似乎没有在等待结果useFetchCountries()
如果不启动整个演示,我无法对此进行测试,但我建议进行这些更改,以便您在测试中等待 graphQL 调用:
// /src/composables/useFetchCountries.spec.ts
it("retrieves all a couple countries", async () => {
const { countries } = await useFetchCountries();
...
});
// /src/composables/useFetchCountries.ts
const useFetchCountries = async () => {
const { result } = await useQuery<GetCountriesQuery>(GET_COUNTRIES);
...
}
并且还使您的功能.useQuery
async
评论
我几天前也开始学习 VueJS,所以我不知道这是否是我的解决方案的好实践
我在 @vue/test-utils 文档上发现有一个方法可以调用,命名为:flushPromises,这是解释:
flushPromise 刷新所有已解析的 promise 处理程序。这有助于确保异步操作(如 promise 或 DOM 更新)在对它们进行断言之前已经发生。
这是官方文档的链接
我意识到您可以在不使用 msw 的情况下模拟响应,仅使用 mock-apollo-client 库,我遵循了他们在文档中的这个示例。
所以,话虽如此,我这样做了,它对我有用:
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { flushPromises } from '@vue/test-utils'
import { createMockClient } from 'mock-apollo-client'
import { provideApolloClient } from '@vue/apollo-composable'
import { useFetchCountries, GET_COUNTRIES } from './useFetchCountries'
const coupleCountries = new Map([
["AD", { code: "AD", name: "Andorra", currency: "EUR" }],
["AT", { code: "AT", name: "Austria", currency: "EUR" }],
["AX", { code: "AX", name: "Åland", currency: "EUR" }],
["BE", { code: "BE", name: "Belgium", currency: "EUR" }],
["BL", { code: "BL", name: "Saint Barthélemy", currency: "EUR" }],
]);
describe('About View', () => {
beforeEach(() => {
const mockClient = createMockClient()
mockClient.setRequestHandler(
GET_COUNTRIES,
() => Promise.resolve({ data: { countries: Array.from(coupleCountries.values()) }}));
provideApolloClient(mockClient)
})
afterEach(() => {
vi.restoreAllMocks()
})
it("retrieves all a couple countries", async () => {
const { countries } = await useFetchCountries();
await flushPromises();
const dummy = [
{ code: "AD", name: "Andorra", currency: "EUR" },
{ code: "AT", name: "Austria", currency: "EUR" },
{ code: "AX", name: "Åland", currency: "EUR" },
{ code: "BE", name: "Belgium", currency: "EUR" },
{ code: "BL", name: "Saint Barthélemy", currency: "EUR" },
];
expect(countries.value).toStrictEqual(dummy);
});
})
评论