Testing API Functions

In our workflow repo, the register function makes an API call to create a new user account:

// auth.js
import { URL } from "../../constants/api.js";

export async function register(user) {
  const url = `${URL}auth/register`;

  const options = {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(user),
  };

  const response = await fetch(url, options);
  const json = await response.json();

  if (!response.ok) {
    throw new Error("Registration failed");
  }

  return json;
}

This function:

  • Takes a user object with registration details
  • Makes a POST request to the registration endpoint
  • Returns the new user data if successful
  • Throws an error if the request fails

Testing Success and Failure

Just like in lesson 4, we’ll use a mock fetch function to test both successful and failed registration:

import { expect, describe, it, beforeEach } from "vitest";
import { register } from "./auth";

describe("register", () => {
  beforeEach(() => {
    global.fetch = vi.fn();
  });

  it("returns the user data when registration succeeds", async () => {
    const successResponse = {
      id: 1,
      name: "John Smith",
      email: "john@stud.noroff.no",
    };

    fetch.mockResolvedValue({
      ok: true,
      json: () => Promise.resolve(successResponse),
    });

    const result = await register({
      name: "John Smith",
      email: "john@stud.noroff.no",
      password: "password123",
    });

    expect(result).toEqual(successResponse);
  });

  it("throws an error when registration fails", async () => {
    fetch.mockResolvedValue({
      ok: false,
      json: () => Promise.resolve({ message: "Email already exists" }),
    });

    await expect(
      register({
        name: "John Smith",
        email: "john@stud.noroff.no",
        password: "password123",
      })
    ).rejects.toThrow("Registration failed");
  });
});

Let’s break down what we’re testing:

  1. Success case:

    • Mock fetch to return a successful response
    • Call register with user details
    • Verify we get back the expected user data
  2. Failure case:

    • Mock fetch to return a failed response
    • Call register with user details
    • Verify it throws the expected error

Note that we’re testing the function’s behavior (what it returns or throws) rather than implementation details (like what URL it calls).

What We Learned

  • Used mocks to test functions that make API calls
  • Tested both successful and failed API responses
  • Focused on testing behavior rather than implementation details
  • Used beforeEach to reset our mocks between tests

Video

Even though fetch does exist in the Node environment, we still want to mock API calls in unit tests.

Video

Tags: