Introduction

Updated May 7, 2026

Run TypeScript code blocks from your markdown docs as real vitest or jest tests. Mark a fenced block with test, and it becomes a runnable assertion — no separate test file, no boilerplate, no drift between docs and code.

Install

pnpm add -D testdocs
# or
npm install --save-dev testdocs

Write a doctest

Add test to any TypeScript fence in any .md file. The block's body becomes the body of an it() named after the nearest preceding heading. describe, it, and expect are injected for you.

```ts test
import { sum } from "./sum.ts"
expect(sum(1, 2)).toBe(3)
```

Untagged ts blocks render normally and are not run, so you can mix illustrative snippets and runnable assertions in the same article.

Wire it into your test runner

For vitest (or vite-plus), add the plugin and put .md in test.include:

vite.config.ts
import testdocs from "testdocs/vite"
import { defineConfig } from "vitest/config"

export default defineConfig({
  plugins: [testdocs()],
  test: {
    include: ["**/*.test.ts", "docs/**/*.md"],
  },
})

For jest, register the transformer:

jest.config.ts
export default {
  transform: { "\\.md$": "testdocs/jest" },
  testMatch: ["**/*.test.ts", "**/docs/**/*.md"],
}

Run your usual pnpm test — your .md files now run alongside your *.test.ts files. See Writing tests for the marker syntax, Running tests for what to expect at the terminal, and Configuration for runner-specific notes.

Created with and Livemark