Exercise 1: Unit-Testing Optimistic Concurrency With a Mock — Possible Solution ================================================================================= First, make savePost (Chapter 1) take the client as a parameter: // src/posts.ts export async function savePost(db: PrismaClient, id: number, loadedVersion: number, content: string) { const result = await db.post.updateMany({ where: { id, version: loadedVersion }, data: { content, version: { increment: 1 } }, }); if (result.count === 0) { throw new Error("Someone else changed this post. Reload and try again."); } } // test/savePost.unit.test.ts import { describe, it, expect, beforeEach } from "vitest"; import { mockDeep, mockReset, type DeepMockProxy } from "vitest-mock-extended"; import type { PrismaClient } from "../generated/prisma/client"; import { savePost } from "../src/posts"; const db: DeepMockProxy = mockDeep(); beforeEach(() => mockReset(db)); describe("savePost", () => { it("saves when the version still matches", async () => { db.post.updateMany.mockResolvedValue({ count: 1 }); await expect(savePost(db, 5, 3, "new text")).resolves.toBeUndefined(); expect(db.post.updateMany).toHaveBeenCalledWith({ where: { id: 5, version: 3 }, data: { content: "new text", version: { increment: 1 } }, }); }); it("throws when someone else saved first", async () => { db.post.updateMany.mockResolvedValue({ count: 0 }); await expect(savePost(db, 5, 3, "stale text")).rejects.toThrow("Someone else changed"); }); }); WHY THIS WORKS AS AN ANSWER ------------------------------ The function's only decision is "was a row updated or not?", so controlling the count with mockResolvedValue exercises both branches without a database. The first test also checks the exact where clause, which is the part that makes the concurrency check work — if someone removed "version" from where, this test would fail. What it can't prove is that the database really skips the update on a version mismatch; that needs an integration test.