DEV Community

Cover image for Should You Unit Test Glue Files? A Practical Decision Matrix

Should You Unit Test Glue Files? A Practical Decision Matrix

Have you ever spent an entire afternoon writing a unit test for a five-line setup component?

You mock three context providers, an Auth.js session hook, and a router adapter—only to assert that a button renders in the DOM. At that point, it’s worth asking:

Are we testing our application, or just testing our mocks?

In React applications, not every file benefits equally from isolated unit tests—but figuring out which ones those are isn't always obvious. Over time, I’ve moved away from chasing high coverage numbers for its own sake toward a more pragmatic framework for balancing unit tests (with Vitest or Jest) and End-to-End integration tests (with Playwright).

Here is how I think about testing wiring code without grinding developer velocity to a halt.


What Are "Glue Files"?

"Glue files" isn't an official software engineering term. It's simply the name I use for files whose primary responsibility is connecting parts of an application rather than implementing business logic.

Most React developers will recognize these files instantly in their codebases:

  • Entry & Wiring Files: main.tsx, App.tsx, providers.tsx, layout.tsx
  • Routing & Client Config: router.tsx, queryClient.ts, middleware.ts
  • Store Initialization: store.ts (when primarily configuring Redux/Zustand rather than implementing custom middleware or complex state logic)
  • Auth & Localization Wrappers: <SignInButton/> wrappers around Auth.js or Firebase, and language switchers wrapping next-intl
  • UI Primitives: Base design system elements (components/ui/* built over Radix UI or Tailwind primitives)

These files act as the "mortar" holding your external dependencies, global state trees, and visual layout together.


The Hidden Cost of Unit Testing Pure Glue Files

Writing unit tests for pure domain functions is fast and rewarding. Writing unit tests for glue files often leads to three common headaches:

1. 90% Mock Setup, 10% Actual Assertion

To unit test a file that simply instantiates a client or wires up a third-party hook, you often end up writing dozens of lines of mock setup code just to test a few lines of JSX:

// tests/AuthButton.test.tsx
import { render, screen } from '@testing-library/react';
import { vi } from 'vitest';

// Mocking external library hooks and routers
vi.mock('next-auth/react', () => ({
  useSession: () => ({ data: null, status: 'unauthenticated' }),
  signIn: vi.fn(),
}));

vi.mock('next/navigation', () => ({
  useRouter: () => ({ push: vi.fn() }),
}));

test('renders sign-in button', () => {
  render(<SignInButton />);
  expect(screen.getByText('Sign In')).toBeInTheDocument();
});

Enter fullscreen mode Exit fullscreen mode

When 90% of a test file consists of vi.mock(), you aren't really testing application behavior—you are mostly testing your ability to write mocks.

2. High Fragility with Low Return

Because these tests are tightly bound to the internal signatures of third-party libraries, updating a dependency often breaks your unit tests even if the actual user-facing feature works perfectly fine.

3. Testing Framework Behavior Instead of Your Logic

Maintainers of packages like TanStack Query, Radix UI, or React Router already maintain extensive unit test suites. Writing unit tests to verify that <QueryClientProvider client="{queryClient}"> passes down context means you're often verifying framework behavior rather than your own application's logic.


The Decision Matrix: Does This File Make Decisions?

In my experience, asking this single question eliminates most debates about whether a file deserves unit tests: Does this file make decisions?

┌─────────────────────────────────────┐
│    Does this file make decisions?   │
└─────────────────────────────────────┘
                 │
        ┌────────┴────────┐
        │                 │
       Yes               No
        │                 │
        ▼                 ▼
┌───────────────┐   ┌────────────────────┐
│   Unit Test   │   │ Is it wiring only? │
│ Vitest / Jest │   └─────────┬──────────┘
└───────────────┘             │
                       ┌───────┴────────┐
                       │                │
                      Yes              No
                       │                │
                       ▼                ▼
             Verify Through       Re-evaluate File
             Integration / E2E    Responsibilities
              (Playwright)

Enter fullscreen mode Exit fullscreen mode

Seeing the Matrix in Practice

Let’s look at two concrete examples to see how this decision rule works in real code.

Example 1: queryClient.ts

Pure Wiring (Skip Unit Tests):

import { QueryClient } from '@tanstack/react-query';

export const queryClient = new QueryClient();

Enter fullscreen mode Exit fullscreen mode
  • Decision Check: Does this file make decisions? No.
  • Strategy: Skip unit testing. Verify data fetching behavior through Playwright integration or end-to-end smoke tests.

Contains Decision Logic (Unit Test It!):

import { QueryClient } from '@tanstack/react-query';

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: process.env.NODE_ENV === 'production' ? 3 : false,
    },
  },
});

Enter fullscreen mode Exit fullscreen mode
  • Decision Check: Does this file make decisions? Yes. It evaluates the current environment to choose retry behavior.
  • Strategy: Write a Vitest unit test to verify that retries evaluate correctly across environments.

Example 2: router.tsx

Static Routes (Skip Unit Tests):

export const router = createBrowserRouter([
  { path: '/', element: <Home /> },
  { path: '/dashboard', element: <Dashboard /> },
]);

Enter fullscreen mode Exit fullscreen mode
  • Decision Check: Does this file make decisions? No. It's purely declarative static wiring.

Conditional Routing (Unit Test It!):

export function getAppRoutes(userRole: string) {
  return createBrowserRouter([
    { path: '/', element: <Home /> },
    ...(userRole === 'admin' ? [{ path: '/admin', element: <AdminPanel /> }] : []),
  ]);
}

Enter fullscreen mode Exit fullscreen mode
  • Decision Check: Does this file make decisions? Yes. It computes routes based on role permissions.

Quick Checklist: When Should You Unit Test a Glue File?

My default rule is: In most cases, I don't unit test pure glue files because they don't contain business logic.

However, a glue file becomes worth unit testing as soon as it:

  • ✅ Contains branching or conditional logic (if / else, ternary checks)
  • ✅ Transforms configuration dynamically at runtime
  • ✅ Chooses behavior based on environment variables
  • ✅ Applies feature flags or user permissions
  • ✅ Has custom calculation logic that can fail independently

Otherwise, skip the unit test and verify it at the integration or E2E level.


Final Thoughts

This isn't a rigid rule—it's a heuristic. Every codebase is different, and your testing strategy should reflect your team's priorities, risk tolerance, and architecture.

Coverage tells you how much code executed. Good tests tell you how confident you should be when shipping.

If there's one takeaway to keep in mind when designing your test suite, it's this:

Code deserves tests in proportion to the decisions it makes.
Business logic makes decisions.
Glue code makes connections.
Test them accordingly.

Top comments (0)