Write Vibe Tests
mistralai/mistral-vibe
Guides writing or refactoring tests for the Mistral Vibe CLI agent so they check behavior through stable boundaries, like tool invocation or saved session shape, instead of internal calls.
Teaches the Nullables pattern for testing without mocking libraries: production classes with an off switch, stubbed only at the third-party edge.
$ npx skills add lexler/skill-factory --skill nullables -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install lexler/skill-factory nullables --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/lexler/skill-factory.git skills-src && mkdir -p .claude/skills && cp -r skills-src/output_skills/testing/nullables .claude/skills/nullables && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "nullables" agent skill from https://github.com/lexler/skill-factory/tree/main/output_skills/testing/nullables into .claude/skills/nullables/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "nullables", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/lexler/skill-factory/tree/main/output_skills/testing/nullablesType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add lexler/skill-factory --skill nullables -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install lexler/skill-factory nullables --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/lexler/skill-factory.git skills-src && mkdir -p .agents/skills && cp -r skills-src/output_skills/testing/nullables .agents/skills/nullables && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "nullables" agent skill from https://github.com/lexler/skill-factory/tree/main/output_skills/testing/nullables into .agents/skills/nullables/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "nullables", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add lexler/skill-factory --skill nullables -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install lexler/skill-factory nullables --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/lexler/skill-factory.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/output_skills/testing/nullables .cursor/skills/nullables && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "nullables" agent skill from https://github.com/lexler/skill-factory/tree/main/output_skills/testing/nullables into .cursor/skills/nullables/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "nullables", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/lexler/skill-factory.git --path output_skills/testing/nullables--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add lexler/skill-factory --skill nullables -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install lexler/skill-factory nullables --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/lexler/skill-factory.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/output_skills/testing/nullables .gemini/skills/nullables && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "nullables" agent skill from https://github.com/lexler/skill-factory/tree/main/output_skills/testing/nullables into .gemini/skills/nullables/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "nullables", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install lexler/skill-factory nullablesInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add lexler/skill-factory --skill nullables -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/lexler/skill-factory.git skills-src && mkdir -p .github/skills && cp -r skills-src/output_skills/testing/nullables .github/skills/nullables && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "nullables" agent skill from https://github.com/lexler/skill-factory/tree/main/output_skills/testing/nullables into .github/skills/nullables/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "nullables", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add lexler/skill-factory --skill nullables -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install lexler/skill-factory nullables --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/lexler/skill-factory.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/output_skills/testing/nullables .opencode/skills/nullables && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "nullables" agent skill from https://github.com/lexler/skill-factory/tree/main/output_skills/testing/nullables into .opencode/skills/nullables/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "nullables", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
nullablesTeaches the Nullables pattern for testing without mocking libraries: production classes with an off switch, stubbed only at the third-party edge.
Nullables are production classes with an off switch. A class that touches external I/O anywhere in its dependencies offers `create()` for the real thing and `createNull()` for a version with I/O disabled while everything else runs normally. Tests are narrow, focused on one class, sociable, with real dependencies, and state-based, asserting outputs and state rather than method calls. Mocking libraries and dependency injection frameworks are not used.
The only stub sits at the lowest point, the third-party edge. A high-level wrapper abstracts one service and speaks domain language, a low-level wrapper abstracts one technology, and only the low-level wrapper holds the embedded stub that returns canned data. Parsing, mapping and normalization code you wrote must stay above that cut so tests exercise it. Reference files cover architecture, high- and low-level wrappers, consuming Nullables, migration and utilities, and the excerpt is cut off before the interaction moves.
4 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 8017333. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are javascript).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Nullables Testing Pattern loads about 2.2k tokens when it runs, and up to ~16k if it reads all its reference files. Until then it costs about 72 tokens; SKILL.md has 1,127 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from lexler/skill-factory at commit 8017333, republished under its Apache-2.0 licence (© lexler). 1,127 words, ~2,212 tokens.
.claude/skills/nullables/SKILL.md (or your agent's skills folder). This skill also uses 8 other files; get the full folder from GitHub.STARTER_CHARACTER = ⭕️
Nullables are production code with an off switch: classes with external I/O anywhere in their dependencies offer create() (real) and createNull() (I/O disabled, everything else runs normally). Tests are narrow (focused on one class), sociable (dependencies run for real), and state-based (assert outputs and state, never method calls). Don't use mocking libraries or DI frameworks — Nullables make them unnecessary.
Stub at the lowest point — the third-party edge — never your own code:
OrderService → PaymentClient → HttpClient → third-party lib
app code high-level low-level ✂ stubbed when nulled
wrapper wrapperPaymentClient is a high-level wrapper: it abstracts one service and speaks domain language.HttpClient is a low-level wrapper: it abstracts one technology and is generic and highly reusable.create() wires the real library (node http, RestTemplate); createNull() wires an embedded stub — your code, returning canned data, doing no I/O.With mocks, you only mock code you own; with Nullables, you only stub code you don't own. Only the bottom layer has a stub — one per technology. Everything above runs real in tests, so a bug anywhere in your code turns tests red. Mocking your own classes breaks that chain: mocked code never runs, and its bugs hide behind green tests.
An invented internal seam is the same break in disguise: cutting at rows() → List<Row> instead of the driver puts your mapping loop below the seam, where nulled tests never run it. The test: any parsing, mapping, or normalization you wrote must sit above the cut. When the third-party API is a chain of objects, mirror it — one stub class can play the whole chain (see the low-level wrapper files).
Every class that talks to infrastructure anywhere in its dependencies offers the same two factory methods:
Clock.create() // production: the real system clock
Clock.createNull({ now: "2024-01-01" }) // test: frozen time, no external stateTests interact with a nulled instance through three moves:
Reads — configure what the world answers, as createNull(...) parameters in the caller's domain terms: PaymentClient.createNull({ approved: false }), DieRoller.createNull([3, 5, 1]). A single value repeats forever; a list is consumed in order, then fails fast. An error is just another configured response: createNull([{ error: "boom" }]).
Writes — observe what the code sent, as domain data (track the data, not the rendered string):
const emails = emailer.trackOutput();
await service.register("a@b.com");
assert.deepEqual(emails.data, [{ to: "a@b.com", subject: "Welcome" }]);The same tracker can prove a negative — a test where registration is refused asserts emails.data is []: no email went out.
Pushed events — fire a simulated incoming event through the same handler path a real event takes: network.simulateMessage("client-1", "Hello").
These ride on two tiny utilities, OutputListener/OutputTracker and ConfigurableResponses. When the codebase lacks them, add them — example implementations in utilities.md.
Start from the code you need to test. For a whole system, pick one class and repeat; conversion order across many classes is in migration.md.
createTestInstance() with safe overridable defaults. If it holds an infrastructure object, default it to the nulled version.createNull() → go to step 4.createNull() → step 2.createNull, Stubbed, infrastructure/) → reuse it, go to step 3.create() and createNull() that compose its nulled dependencies — follow building-high-level-wrappers.md; its recipe carries the decomposition and tracker moves that keep layers honest, and this is where abstractions leak if you rush. Done when every class between the edge and the class under test has both factories, configuration in its own language decomposed downward, and its write channel tracked.Converting a mock-based suite → migration.md. Improving existing nullables → walk their layers and check each against The cut and the rules below, plus: no leftover throwaway stubs. Structuring a new app around this (optional) → architecture.md.
Before committing a converted class, walk these as a checklist against it.
create() wires production, createNull() wires nulled — both factories live on the wrapped class, never on the stub. The plain constructor is the test seam: tests use it to inject dependencies they hold handles on.PaymentClient.createNull({ approved: false }), not HTTP statuses. Each layer decomposes its configuration into its dependency's language.createNull() always works: every parameter has a safe default, so one call nulls the whole dependency chain from the top (parameterless instantiation)."Nulled HttpClient default body", status 503, timezone Australia/Lord_Howe; stub errors self-name too ("Nulled Jdbc: the database is down"). Nothing breaks on these, but a test that accidentally depends on one sees obviously fake data instead of passing by luck. Collections default empty — the default world is empty; absurd entries would be mistakable for real data. Failing fast is reserved for overrunning explicit configuration: an exhausted response list throws "No more responses configured…".IRRELEVANT_* defaults, returning a bag of results and trackers. A signature change hits one place.nulled flag forking the wrapper's logic with if-branches — nulling swaps the wrapped dependency at the seam; the wrapper keeps one code path.© lexler, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 8 other files (references) in output_skills/testing/nullables of lexler/skill-factory.
Open the folder on GitHubat commit 8017333
Nullables Testing Pattern next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Nullables Testing Pattern this skilllexler/skill-factory | 239 | — | ~2.2k | Automated safety check: Pass | Apache-2.0 | |
| Write Vibe Testsmistralai/mistral-vibe | 5.1k | — | ~1.2k | Automated safety check: Pass | Apache-2.0 | |
| cmux Testing Rulesdisler/learning-cmux-with-agents | 115 | — | ~1.2k | Automated safety check: Pass | MIT | |
| Nestjs Expertdavila7/claude-code-templates | 32k | 6 repos | ~5.3k | Automated safety check: Notes | MIT | |
| Testing OpenLogi UIAprilNEA/OpenLogi | 23k | — | ~1.1k | Automated safety check: Pass | Apache-2.0 | |
| Async Test And Doc Syncdiscord-php/DiscordPHP | 1.1k | — | ~3.4k | Automated safety check: Notes | MIT |
mistralai/mistral-vibe
Guides writing or refactoring tests for the Mistral Vibe CLI agent so they check behavior through stable boundaries, like tool invocation or saved session shape, instead of internal calls.
disler/learning-cmux-with-agents
Testing rules for the cmux Swift codebase: Swift Testing as the default framework, a two-commit regression policy, and tests that check runtime behavior, not source text.
davila7/claude-code-templates
Nest.js framework expert specializing in module architecture, dependency injection, middleware, guards, interceptors, testing with Jest/Supertest, TypeORM/Mongoose integration, and Passport.js…
AprilNEA/OpenLogi
Verifies OpenLogi's native GPUI interface with focused tests, the component gallery and a mock agent, choosing the evidence that fits each change.
discord-php/DiscordPHP
Maintain test and documentation alignment — PHPUnit tests, async testing patterns, PHPDoc contracts, guide pages, and documentation workflow.
CloudAI-X/claude-workflow-v2
Designs and implements testing strategies for any codebase. An agent skill from CloudAI-X/claude-workflow-v2.
lexler/skill-factory
Creates C4 model diagrams at every zoom level, from system landscape to code, in ASCII, Mermaid or Structurizr, for designing or documenting software architecture.
lexler/skill-factory
Plans and launches Claude Code agent teams with distinct roles, right-sized tasks and detailed spawn prompts, and says when subagents or worktrees fit better.
lexler/skill-factory
Guides writing and debugging Claude Code status line scripts that read session JSON from stdin and print one line of text.
lexler/skill-factory
Catalog of obstacles, anti-patterns and patterns for working with AI coding agents, covering context management and reliability, from a published patterns collection.
lexler/skill-factory
Writes snapshot-style approval tests in Python, JavaScript, TypeScript or Java, comparing output against an approved file instead of writing individual assertions.
lexler/skill-factory
Find where a codebase actually costs time by mining its git history (Tornhill hotspot analysis).
Categories
Teaches the Nullables pattern for testing without mocking libraries: production classes with an off switch, stubbed only at the third-party edge. Nullables are production classes with an off switch. A class that touches external I/O anywhere in its dependencies offers `create()` for the real thing and `createNull()` for a version with I/O disabled while everything else runs normally.
Nullables Testing Pattern fits situations like: writing unit tests for code that calls HTTP, databases, files, the clock or random; replacing mocking libraries in an existing test suite; making a system testable without a dependency injection framework; speeding up tests that are slow or flaky because of real I/O.
Run `npx skills add lexler/skill-factory --skill nullables -a claude-code`. Or copy the skill folder (output_skills/testing/nullables in lexler/skill-factory) into .claude/skills/nullables in your project. Claude Code loads it when a task matches its description.
Run `npx skills add lexler/skill-factory --skill nullables -a codex`. Or copy the skill folder (output_skills/testing/nullables in lexler/skill-factory) into .agents/skills/nullables in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add lexler/skill-factory --skill nullables -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/nullables, .gemini/skills/nullables, .github/skills/nullables and .opencode/skills/nullables in your project.
SKILL.md names no scripts, command-line tools or credentials: Nullables Testing Pattern is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Nullables Testing Pattern is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.2k tokens (SKILL.md is roughly 8.8k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 14k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Nullables Testing Pattern: Write Vibe Tests (mistralai/mistral-vibe, 5.1k stars), cmux Testing Rules (disler/learning-cmux-with-agents, 115 stars), Nestjs Expert (davila7/claude-code-templates, 32k stars) and Testing OpenLogi UI (AprilNEA/OpenLogi, 23k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
lexler (a GitHub user) maintains it in lexler/skill-factory, which has 239 GitHub stars. The repository holds 25 skills in this directory. The repository was last updated on August 26, 2026.
Source: lexler/skill-factory on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.