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

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


0

I made a simple dummy application as in the end it will fit in a much bigger enterprise application, but I first made a proof of concept to make sure it works… and it doesn’t yet! I’m at a loss why this is.

I’ve followed the documentation on https://mswjs.io/ for getting the mock in a test runner. So I’ve got a src/mocks/server.ts file where I’m setting up the server with the handlers. I’ve got a src/setup-file.ts for Vitest which is stated in the vitest.config.ts setupFiles property to do the beforeAll, afterEach and afterAll stated in the MSW documentation. I see in the console when running the application that requests are mocked as it does intercept it. However, it does nothing in the test runner.

I’ve got a simple composable that fetches countries from a public graphql endpoint (just for testing :-)).

// /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,
  };
};

This is then retrieved in the 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>

Then my test for the composable is as follows (:

// /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);
  });
});

All right, now finally what I have in the handlers to intercept the GetCountries query and return a mock.

// /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()),
      },
    });
  }),
];

However because of Vue computes, countries is already undefined upon running the test! I have no clue how to fix this as I only started working with Vue a little while ago. I get the main gist of it, however with writing more advanced tests like this, I’m completely in the dark.

In the bigger application there are certain methods in the composable that do things with the data that is retrieved from the useQuery. Those methods I want to test to ensure they are proper and not changed later on causing side effect. Any ideas how to fix the test above to make sure the mocked data is actually inside the test?

1 Answer
1


0

You don’t seem to be awaiting the result of useFetchCountries()

I can’t test this without spinning up a whole demo, but I’d recommend these changes so that you await the graphQL call in your tests:

  // /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);
    ...
  }

And also make your useQuery function async.



Leave a Reply

Your email address will not be published. Required fields are marked *