Vue、Vitest 和 MSW:模拟和测试 GraphQL 查询导致无法组合

Vue, Vitest and MSW: Mocking and testing a GraphQL query result in composable not possible

提问人:dunct 提问时间:11/14/2023 最后编辑:dunct 更新时间:11/20/2023 访问量:74

问:

编辑

我做了一个 stackblitz 示例,其中我拥有最多的问题。你可以在这里找到它:https://stackblitz.com/edit/vitejs-vite-axxjsu?file=src%2Fcomposables%2FuseFetchCountries.spec.ts

编辑结束

我做了一个简单的虚拟应用程序,因为最终它将适合一个更大的企业应用程序,但我首先做了一个概念验证以确保它有效......它还没有!我不明白为什么会这样。

我已经按照 https://mswjs.io/ 上的文档在测试运行程序中获取模拟。因此,我有一个文件,用于设置带有处理程序的服务器。我有一个 for Vitest,它在 setupFiles 属性中说明,并在 MSW 文档中说明。我在运行应用程序时在控制台中看到请求被模拟,因为它确实拦截了它。但是,它在测试运行程序中不执行任何操作。src/mocks/server.tssrc/setup-file.tsvitest.config.tsbeforeAllafterEachafterAll

我有一个简单的可组合项,可以从公共 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 检索到的数据执行操作。我想测试这些方法,以确保它们是正确的,并且以后不会改变,从而产生副作用。如何修复上面的测试以确保模拟数据实际上在测试中的任何想法?

vue.js 单元测试 图形QL 垃圾 可组合

评论


答:

0赞 paddyfields 11/14/2023 #1

你似乎没有在等待结果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);
    ...
  }

并且还使您的功能.useQueryasync

评论

0赞 dunct 11/15/2023
谢谢你的回答!一旦我回到家,我会将其标记为答案,如果确实是这样,或者创建一个演示供您查看,以便我们深入了解。再次感谢!
0赞 dunct 11/15/2023
嗨,又来了!我在我的问题中添加了一个 stackblitz 示例以重新创建它。创建 promise 不是因为它,因为 vue/apollo-composable 的 useQuery 没有返回 promise。也许这就是问题所在......我会更深入地研究这个问题,但也像我说的那样分享了问题中的链接!
1赞 Henry Zarza 11/20/2023 #2

我几天前也开始学习 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);
  });
})

评论

0赞 dunct 11/20/2023
谢谢!这确实奏效了。有点遗憾的是,还没有找到带有 MSW 的工作项目,因为当后端尚无法创建端点时,它还有助于模拟开发环境的数据。无论如何,谢谢你!这确实解决了测试功能的问题,就像我想要的那样!