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.
Bhavishya Sahdev
Author
Evaluating Unit Tests on API Wrappers: Practical Advice
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:
typescriptasync 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:
- Confirming
customFetch
is called with expected arguments. - 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

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

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
- Fetch API - MDN Web Docs
- Jest Mock Functions
- Testing Library Documentation
- Kent C. Dodds. "The Testing Trophy & Pyramid." Blog. https://kentcdodds.com/blog/the-testing-trophy-and-pyramid