一键导入
write-nix-tests
Write tests for nix scripts. Use when creating or modifying nix scripts, or generating test.nix files.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Write tests for nix scripts. Use when creating or modifying nix scripts, or generating test.nix files.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | write-nix-tests |
| description | Write tests for nix scripts. Use when creating or modifying nix scripts, or generating test.nix files. |
A nix script lives in a directory with:
nix-script-name/
├── default.nix # Main nix script
├── test.nix # Tests using pkgs.lib.runTests
Tests use nix eval -f test.nix — success when output is [ ] (empty list).
let
npinsed = import <path-to-npins>; # relative path, e.g. ../../npins
pkgs = import npinsed.nixpkgs {};
my-derivation = import ./.;
in pkgs.lib.runTests {
test-name = {
expr = <expression>;
expected = <expected value>;
};
}
Run tests with: nix eval -f test.nix
npins and luajit-proDo not use <npins> or <luajit-pro> angle brackets — they require NIX_PATH to be set and break when nix-build is run from outside the project directory.
Instead, use relative paths from each file's location to the repo root directories:
| File location | npins path | luajit-pro path |
|---|---|---|
foo/default.nix | ../npins | ../luajit-pro |
foo/bar/default.nix | ../../npins | ../../luajit-pro |
foo/bar/baz/default.nix | ../../../npins | ../../../luajit-pro |
Example for a file at depth 2 (e.g., lib/mimalloc2/default.nix):
let
npinsed = import ../../npins;
pkgs = import npinsed.nixpkgs {};
in ...
Example with luajit-pro at depth 3 (e.g., lib/lua-modules/tcc/default.nix):
let
npinsed = import ../../../npins;
pkgs = import npinsed.nixpkgs {};
luaPackages = (import ../../../luajit-pro).pkgs;
in ...
builtins.readDir to explore directory structurepkgs.lib.filterAttrs (_: v: v != null) to drop unset optional fieldspkgs.lib.mkIf to conditionally apply configbuiltins.tryEval to test code that should faildefault.nix, use import ./. instead of import ./default.nixIf default.nix takes arguments (e.g., { complete }:), import it with different argument values and test each variant. Combine all assertions into a single pkgs.lib.testAllTrue:
let
npinsed = import ../../npins;
pkgs = import npinsed.nixpkgs {};
my-derivation = import ./. { arg1 = false; };
my-derivation-complete = import ./. { arg1 = true; };
in pkgs.lib.runTests {
test-lib = pkgs.lib.testAllTrue [
((builtins.readDir "${my-derivation}/lib") ? "libfoo.so")
((builtins.readDir "${my-derivation-complete}/lib") ? "libfoocomplete.so")
];
}
Run tests with: `nix eval -f test.nix`
**Building with arguments:**
```bash
nix-build lib/lua-modules/lsqlite/default.nix --arg complete false -o result-tmp
nix-build lib/lua-modules/lsqlite/default.nix --arg complete true -o result-tmp
Before writing tests, always build the derivation first:
nix-build <path-to-default.nix> -o result-tmp
# Explore subdirectories to find actual output files
This reveals:
Then write tests for actual user-facing files: C libs (.so, .a) and Lua scripts (.lua)
Do not write trivial attribute tests (e.g. version, src, pname)
Do not write trivial folder existence tests (e.g. test whether folder lib/, bin/, lua/ exists)
For single files, use builtins.pathExists which is simpler than builtins.readDir:
test-libs = pkgs.lib.testAllTrue [
(builtins.pathExists (import ./. + /lib/libfoo.so))
];
Simple existence test:
test-executable = pkgs.lib.testAllTrue [
(builtins.pathExists (my-derivation + /bin/myexe))
];
Run executable and verify output:
test-executable-runs = let
output = pkgs.runCommand "test-myexe" {
# env is optional, only use it if needed
env = {
MY_ENV_VAR = toString npinsed.some-path;
};
} ''
${my-derivation}/bin/myexe --help > $out 2>&1 || true
'';
content = builtins.readFile output;
in pkgs.lib.testAllTrue [
(builtins.stringLength content > 100)
(pkgs.lib.hasPrefix "<some-pattern>" content)
];
Notes:
env attribute to set environment variables required by the executableenv values must be derivations, strings, booleans, or integers — use toString for paths2>&1) to capture all output|| true if the executable may return non-zero exit code (e.g., --help often does)builtins.stringLength (not pkgs.stringWidth) to check string lengthCombine all tests expecting true into a single pkgs.lib.testAllTrue:
# correct
test-lib = pkgs.lib.testAllTrue [
(dir ? "libfoo.a")
(dir ? "libfoo.so")
];
# wrong — double-wrapping
test-lib = {
expr = pkgs.lib.testAllTrue [ ... ];
expected = true;
};
npinsed for the npins attribute set to distinguish from pkgs.npinspkgs for the nixpkgs instance derived from npinsednixpkgs (or pkgs) source code: $(nix eval --impure --expr "(import $NPINS_DIRECTORY).nixpkgs.outPath")