Testing API Calls with Vitest

Testing API Functions

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

javascript
	// 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:

javascript
	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