~/TechPurAI
~/tutorials/nextjs-from-scratch/testing-and-deploying
intermediate·part 22 of 22·3 min read

Testing and deploying to production

Updated Aug 16, 2026JavaScript · Next.js

Twenty-one parts in, devnotes has real routing, data fetching, SEO, a comment form, and a sitemap. This last part covers what's worth testing before it ships, and getting it live.

Testing a Server Action directly

bash
npm install -D vitest
ts
// app/actions.test.ts
import { describe, it, expect } from "vitest";
import { submitComment } from "./actions";

describe("submitComment", () => {
  it("rejects a comment under 3 characters", async () => {
    const formData = new FormData();
    formData.set("body", "hi");

    const result = await submitComment("hello-app-router", {}, formData);

    expect(result.error).toBe("Comment needs at least 3 characters.");
  });

  it("accepts a valid comment", async () => {
    const formData = new FormData();
    formData.set("body", "This was a genuinely helpful post.");

    const result = await submitComment("hello-app-router", {}, formData);

    expect(result.success).toBe(true);
  });
});

Part 15's submitComment is a plain async function underneath the "use server" directive — calling it directly in a test, passing a real FormData object, needs no special Next.js test harness, no rendered component, no browser. This is the same principle the Django and DRF series applied to their own validation logic: the highest-value tests are the ones covering real logic (the length check here) rather than trivial rendering.

What's worth testing beyond that

Full page-level and routing behavior is generally better covered by end-to-end tools (Playwright, the same one used to verify pages throughout this very site's own build process) than by unit tests reaching into Next.js's routing internals directly.

Deploying to Vercel

bash
npm install -g vercel
vercel

Vercel is built by the same team as Next.js, and it's the deployment target every rendering strategy from part 17 works on with zero extra configuration — SSG pages served from its CDN, SSR and Server Actions running as serverless functions automatically, ISR's background regeneration handled natively. Running vercel from the project root deploys it directly; connecting the same project to a GitHub repository through Vercel's dashboard adds automatic deployments on every push instead.

Setting environment variables in production

Part 16's .env.local never gets deployed — it's gitignored, and Vercel has no access to a file that was never committed. Every variable from it needs to be set again, explicitly, in the hosting platform's own dashboard (Vercel's Project Settings → Environment Variables, or the equivalent on any other host) — DATABASE_URL and any other real secret, entered there rather than committed anywhere.

Checkpoint

After deploying, check /sitemap.xml and /robots.txt first — both resolving correctly with real post URLs confirms the deployed build has the right data and environment before debugging anything more specific, the same health-check-first instinct the REST API series' own deployment parts both closed on.

What you built

Twenty-two parts back, this was npx create-next-app. It's now a real blog with static and dynamic routes, Server and Client Components used deliberately rather than by default, full SEO metadata and JSON-LD, an optimized image and font pipeline, a working comment form built on Server Actions, middleware, and a sitemap that stays accurate on its own. That shape — server-first by default, client-side JavaScript only where interactivity actually demands it — is the foundation nearly every real Next.js App Router project is built from.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
← previous21. Generating a sitemap and robots.txt