Skip to main content
Back to blog Copy Markdown
· 6 min read ·
nextjs vercel testing ci-cd vitest react

Vercel CI Hell: Fix Next.js Tests Failing in Production

Tests pass locally but fail on Vercel? The real fix is one line: NODE_ENV in vitest.config.ts, plus CI-aware timeouts. My 4 wasted hours, condensed. →

Óscar Gallego

Óscar Gallego

Web Developer

Tests passing locally but failing in Vercel CI environment
On this page

Why do my tests pass locally but fail on Vercel?

When tests work on your machine but die in Vercel’s CI, it’s usually two environment differences: an incorrect NODE_ENV configuration, and timeouts tuned for hardware 5-10x faster than the CI container.

The fast checklist:

  1. Force NODE_ENV to 'test'. Your vitest.config.ts needs env: { NODE_ENV: 'test' }. Vercel might default to production mode, which strips out React’s testing utilities.
  2. Raise timeouts for CI. Vercel’s CI is 5-10x slower than a local machine. Make timeouts environment-aware: const TIMEOUT = process.env.CI ? 10000 : 5000;.
  3. Read the Vercel logs. Look for the specific tests that fail or crawl. The actImplementation is not a function error is often a symptom of the NODE_ENV issue.
  4. Mock heavy operations. No file I/O, no giant test data. Lightweight mocks instead.
  5. Don’t blame the libraries yet. Before assuming version compatibility problems, check your environment configuration. It’s the most common cause.

The rest of this post is how I learned each of those the slow way.

Same code, same dependencies, different results

# Local
$ pnpm test
 All tests pass (181 passed)

# Vercel
 Build failed
Error: actImplementation is not a function

Stack: Next.js 16, React 19, Vitest, Vercel Time wasted: 4 hours chasing the wrong problem Root cause: misunderstanding Vercel’s CI environment

What I thought it was (the embarrassing part)

“React 19 compatibility issue with Testing Library!”

So I built, in order:

  • a 125-line patch script for React internals
  • a custom Vite plugin for module resolution
  • mock files and import redirects

All of it failed on Vercel. All of it unnecessary. Four hours of confident engineering, aimed at a problem that didn’t exist.

The real problem

Your machine is not Vercel’s CI

Local development:

  • Fast SSD, direct I/O
  • Dedicated CPU/memory
  • React loads in development mode
  • Consistent environment

Vercel CI:

  • Networked storage (slower I/O)
  • Shared container resources
  • May default to production mode
  • Cold starts every build

The result: operations take 5-10x longer on Vercel.

The two actual issues

Issue 1: the environment mode

Vercel wasn’t loading React in test mode by default.

The fix:

// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom',

    // ← Forces React test mode
    env: {
      NODE_ENV: 'test',
    },
  },
})

Why this matters: React’s development build includes test utilities, and the production build strips them out. Without NODE_ENV: 'test', React loads incorrectly on Vercel.

Issue 2: resource constraints

CI containers are slower. Operations that feel instant locally time out on Vercel.

The fix:

// CI-aware timeout configuration
const TIMEOUT = process.env.CI ? 10000 : 5000

it('resource-intensive test', async () => {
  // test code
}, TIMEOUT)

A real measurement from this debugging session:

// Creating a 100MB test file
const largeFile = createMockImageFile(
  'large.jpg',
  100 * 1024 * 1024,
  'image/jpeg'
)

// Local: ~500ms
// Vercel CI: ~6-7 seconds (!)

The minimal configuration that works

// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import tsconfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
  plugins: [tsconfigPaths(), react()],
  test: {
    environment: 'jsdom',
    setupFiles: ['./src/test/setup.ts'],
    globals: true,
    css: true,

    // Critical for Vercel CI
    env: {
      NODE_ENV: 'test',
    },

    // Global timeout with CI awareness
    testTimeout: process.env.CI ? 10000 : 5000,
  },
})

Environment-aware test timeouts

// test-config.ts
export const TEST_TIMEOUTS = {
  FAST: process.env.CI ? 5000 : 2000,
  MEDIUM: process.env.CI ? 10000 : 5000,
  SLOW: process.env.CI ? 30000 : 15000,
}

// In your tests
import { TEST_TIMEOUTS } from './test-config'

describe('Feature', () => {
  it('fast operation', async () => {
    // Quick unit test
  }, TEST_TIMEOUTS.FAST)

  it('file I/O operation', async () => {
    // File processing, small data
  }, TEST_TIMEOUTS.MEDIUM)

  it('complex operation', async () => {
    // Large files, network, heavy processing
  }, TEST_TIMEOUTS.SLOW)
})

The diagnostic checklist for next time

When tests fail on Vercel but pass locally:

  1. Check that NODE_ENV: 'test' is set in vitest.config
  2. Read the Vercel build logs for slow tests
  3. Add explicit timeouts to resource-intensive tests
  4. Use environment-aware configuration
  5. Mock expensive operations (file I/O, large data)
  6. Do not start by assuming library incompatibility

Common patterns

// ❌ Assumes local environment
it('test', async () => {
  await heavyOperation()
})

// ✅ Accounts for CI differences
it('test', async () => {
  await heavyOperation()
}, process.env.CI ? 15000 : 5000)
// ❌ Creating real 100MB files
const largeFile = createRealFile(100 * 1024 * 1024)

// ✅ Use lightweight mocks
const largeFile = createMockFile({ size: 100 * 1024 * 1024 })

What four wasted hours taught me

1. Vercel CI is not your MacBook

Design tests with CI in mind from the start: containerized builds, shared resources, slower I/O, different defaults.

2. Configure explicitly

Don’t rely on default behavior. Set NODE_ENV explicitly, set timeouts for slow operations, make configs environment-aware.

3. Error messages can mislead

actImplementation is not a function looked like a React compatibility problem. The actual cause: React loading in the wrong mode due to environment misconfiguration.

4. Design for CI from the start

Write tests knowing they’ll run on slower infrastructure. Account for cold container starts, networked storage, resource throttling, and shared CPU and memory.

What makes Vercel’s build environment different

Containerized builds. Each build runs in an isolated container. Containers share infrastructure resources, and I/O is networked, not local.

Cold starts. Containers spin up fresh for each build. No warm filesystem cache, dependencies downloaded every time.

Resource limits. CPU/memory throttling, shared infrastructure, different performance characteristics.

Environment variables. Different defaults than your local .env. NODE_ENV might not be what you expect, so configure it explicitly.

Configure for it. Don’t fight it.

Vercel’s CI is slower than your machine (5-10x), runs Node.js differently, and needs explicit configuration to behave.

The error message pointed at React. The problem was Vercel’s environment. Knowing the difference is the whole fix, and it’s a one-line config change once you stop patching React internals.

Sources

Related reading: another afternoon lost to a tool silently ignoring my config: the misplaced content.config.ts that ate my Astro schema fields.


P.S. Fought your own round with Vercel’s CI? Tell me what the error message claimed and what the problem actually was, on Twitter/X.