Technology/Software Developmenttestingnextjstypescriptjestsoftware-quality
Evaluating Unit Tests on API Wrappers: Practical Advice

Evaluating Unit Tests on API Wrappers: Practical Advice

Explore best practices for testing Next.js API wrappers with unit, fetch mocks, and integration tests to avoid test bloat.

B

Bhavishya Sahdev

Author

3 min read
Share:

Evaluating Unit Tests on API Wrappers: Practical Advice

Team collaboration over code - photo by ThisIsEngineering on Unsplash
Team collaboration over code - photo by ThisIsEngineering on Unsplash

Introduction

Testing is an essential part of modern software development, but the way we approach it can sometimes feel confusing or even counterproductive. This article addresses a common dilemma encountered while reviewing unit tests for Next.js applications, specifically when primary functions are simple API wrappers calling a generic fetch helper like customFetch.

You will learn how to evaluate the value of such tests, why mocking at certain layers might feel like "testing the mocks," and when integration testing brings the most value.

Scenario Overview

We have primary functions such as getIdentity and sendEvent which call a shared customFetch function:

typescript
async function getIdentity(id: string): Promise<CustomFetchResponse> { return await customFetch(`/api/services/identity/${id}`, 'GET'); } async function customFetch(path: string, method: 'GET' | 'POST' = 'POST', body?: any) { const options = { method, headers: { 'Content-Type': 'application/json' }, body: body ? JSON.stringify(body) : undefined, }; const response = await fetch(path, options); if (response.ok) { // process response } else { // error handling } // ...additional logic }

Tests on the primary functions tend to mock the response of customFetch itself instead of the underlying fetch. This means:

  1. Confirming customFetch is called with expected arguments.
  2. Verifying the mocked response is correctly returned by the primary function.

At first glance, this might seem like you are just testing your mocks.

Are These Tests Valuable or Just Bloat?

When They Make Sense

  • Interface Testing: Ensure primary functions call the generic fetch helper correctly.
  • Return Value Validation: Make sure your wrapper functions handle the returned data properly.

When They Might Be Redundant

  • If your primary functions are simple one-liners delegating entirely to customFetch, and the tests only validate mocked return values, you may be adding little testing value.
  • Excessive mock setup can clutter your test suite and reduce clarity.

Recommended Testing Strategy

1. Unit Test customFetch with fetch Mocking

Mock the native fetch at this level to test request creation, response handling, and error logic.

typescript
// Jest example global.fetch = jest.fn(); describe('customFetch', () => { beforeEach(() => jest.clearAllMocks()); it('should send correct GET request and parse JSON response', async () => { (fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce( new Response(JSON.stringify({ id: '123' }), { status: 200 }) ); const result = await customFetch('/endpoint', 'GET'); expect(fetch).toHaveBeenCalledWith('/endpoint', expect.objectContaining({ method: 'GET' })); expect(result).toEqual({ id: '123' }); }); it('should throw on failed response', async () => { (fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce( new Response(null, { status: 500 }) ); await expect(customFetch('/bad-endpoint')).rejects.toThrow(); }); });

2. Lightweight Tests for Primary Functions Mocking customFetch

Confirm argument passing and basic output transformation, skipping complex logic to avoid duplication.

3. Integration Testing

Build integration tests that run multiple layers together, possibly using a mock server or service worker to replicate realistic network behavior.

This validates actual fetch communication in the app environment.

Visual Guide to Testing Levels

The Testing Pyramid showing different test layers
The Testing Pyramid showing different test layers

Illustration: Testing Pyramid - balancing unit, integration, and E2E tests to maximize reliability and speed.

React Testing Library Octopus Logo
React Testing Library Octopus Logo

Using modern tools like React Testing Library encourages testing behavior over implementation details.

Conclusion

You aren't losing your mind: it's normal to question redundant or superficial tests. The tests mocking customFetch in your case mostly verify call contracts and mocked returns -- which can add limited value when overdone.

Focus on:

  • Thorough unit tests of the customFetch with real fetch mocking.
  • Lean tests on primary API functions to confirm argument passing.
  • Strong integration tests to cover real network scenarios.

This balanced approach keeps your test suite maintainable while confident in your app's behavior.

References

  1. Fetch API - MDN Web Docs
  2. Jest Mock Functions
  3. Testing Library Documentation
  4. Kent C. Dodds. "The Testing Trophy & Pyramid." Blog. https://kentcdodds.com/blog/the-testing-trophy-and-pyramid
Tags:
#testing#nextjs#typescript#jest#software-quality
Share this article

Related Articles

Continue exploring technology/software development topics