Create API Test
When we create a new API, it is advisable to create test cases for a newly created API in order to ensure that all potential scenarios of the API are covered.
-
Create a mock server, we will use https://mswjs.io/
- In
/mocks/api/handlers, specify the request (method, path, and a function that would return the mocked response)
import { rest } from "msw"; import { mockGetCity } from "./city"; const API_BASE_URL = "https://hhstaging.hungryhub.com/api"; export const handlers = [ rest.get(`${API_BASE_URL}/v5/cities.json`, mockGetCity), ];-
Create a new file inside
/mocks/apito provide response resolver, for examplecity.ts. Response resolver is a function that accepts- req, an information about a matching request;
- res, a functional utility to create the mocked response;
- ctx, a group of functions that help to set a status code, headers, body, etc. of the mocked response
-
Create simulations and mocked responses when the API returns
- a successfull response
- a 500 error
- a null response
- an empty string response
- an empty array response
import { ResponseFunction, MockedRequest, RestContext } from "msw"; export const mockGetCity = ( req: MockedRequest, res: ResponseFunction, ctx: RestContext ) => { // simulate if no response if (req.url.searchParams.get("simulate") === "return-nothing") { return res(); } // simulate if backend error 500 if (req.url.searchParams.get("simulate") === "error-500") { return res( ctx.status(500), ctx.json({ message: "Someting went wrong", }) ); } if (req.url.searchParams.get("simulate") === "return-null") { return res(ctx.json(null)); } if (req.url.searchParams.get("simulate") === "return-string") { return res(ctx.json("")); } if (req.url.searchParams.get("simulate") === "return-array") { return res(ctx.json([])); } // simulate if return correct object return res( ctx.json({ data: [ { id: "1", type: "cities", attributes: { name: "Bangkok", home_description: "Hungry Hub Summer Hot Deals 2023", icon: { url: "/uploads/city/icon/1/Bangkok.png", }, country_id: 218, is_mini_homepage: false, country_icon: { url: "/uploads/country/icon/218/Flag_Icon.png", }, total_restaurants: 1549, }, }, ], success: true, message: "", }) ); }; -
Configure server
- Create
mocks/api/server.tsfile - Create a server instance with our request handlers defined earlier
// src/mocks/server.js import { setupServer } from "msw/node"; import { handlers } from "./handlers"; // This configures a request mocking server with the given request handlers. export const server = setupServer(...handlers);
- Create
- In
-
Create test cases
- Create
.spec.tsfile, for examplecity.spec.ts - Import functions from vitest
- Import function that will be tested
- Import mock server that we created earlier
import { expect, test, vi, beforeAll, afterEach, afterAll } from "vitest"; import { getCities } from "./city"; import { server } from "~/mocks/api/server";- Use lifecycle to start, reset, and close the server
beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close());- Mock states if needed, in this case, we need
useLanganduseRuntimeConfig
vi.mock("~/composables/state/lang", () => ({ useLang: () => { return { value: "en", }; }, })); vi.mock("#imports", () => ({ useRuntimeConfig: () => { return { public: { apiDomain: "https://hhstaging.hungryhub.com", }, }; }, }));-
Create test case scenarios when the API returns
- a successfull response
- a 500 error
- a null response
- an empty string response
- an empty array response
- different response from normal response
test("normal fetch", async () => { const result = await getCities(); console.log("res", result.message, result.data); expect(result.isSuccess).toBe(true); }); test("fetch when backend error 500", async () => { const result = await getCities({ simulate: "error-500", }); console.log("res", result.message, result.data); expect(result.isSuccess).toBe(true); }); test("fetch when backend return null", async () => { const result = await getCities({ simulate: "return-null", }); console.log("res", result.message, result.data); expect(result.isSuccess).toBe(true); }); test("fetch when backend not return anything", async () => { const result = await getCities({ simulate: "return-nothing", }); console.log("res", result.message, result.data); expect(result.isSuccess).toBe(true); }); test("fetch when backend return invalid type (string)", async () => { const result = await getCities({ simulate: "return-string", }); console.log("res", result.message, result.data); expect(result.isSuccess).toBe(true); });
- Create