An AI agent skill for writing tests in the `nushell` repo. A developer created an AI agent skill for writing tests in the Nushell repository, emphasizing the use of modern `nu_test_support` infrastructure over older subprocess-style testing. The skill provides guidelines for in-process tests, direct value assertions, and explicit dependencies. | name | nushell-testing | |||||| |---|---|---|---|---|---|---|---| | description | Use when writing or refactoring Nushell Rust tests that use nu test support, NuTester, Playground, rstest, deps, plugins, CompleteResult, or when replacing old nu /nu with plugins tests. | |||||| | license | MIT | |||||| | compatibility | opencode | |||||| | metadata | | Use this skill when writing new tests or refactoring old tests in the Nushell repository. The goal is to use the modern nu test support infrastructure directly, keep tests small and explicit, and avoid subprocess-style testing unless the behavior under test truly crosses the nu binary boundary. The most important rule: do not use nu in new tests. Do not introduce nu , nu with plugins , nu with std , or nu repl code . Some of these APIs may still exist for compatibility in older branches, but new and refactored tests should use test and NuTester instead. Prefer in-process tests. test returns a NuTester that runs Nushell code in-process against the current crate code. This is faster, more deterministic, and avoids stale nu binaries. Assert values, not text rendering. If a command returns a list, record, bool, int, table, duration, or structured error, assert that structured value directly. Do not add to json , to nuon , to text , str join , or manual output collapsing just to make assertion easier. Make required external artifacts explicit. If a test needs a compiled nu binary, use deps NU . If it needs a plugin, use deps NU PLUGIN EXAMPLE or the appropriate plugin dependency. Do not call add nu to path in new code. Keep tests local and readable. Avoid tiny helper functions that hide the testing infra. Prefer a few explicit lines in the test body, rstest cases for repeated shapes, and constants only for large shared data or setup snippets. Return Result . Most Nushell tests should be fn test name - Result , where Result comes from nu test support::prelude:: . Let ? propagate TestError from NuTester and assertion helpers. Use the newest guidance over older examples. Older refactors may use add nu to path ; newer test infra replaces that with deps NU . Most test files should start with the prelude: use nu test support::prelude:: ; Add specific imports when needed: use nu protocol::Signals; use pretty assertions::assert matches; use rstest::rstest; Do not import test value , test record , or test table again; those test macros are already provided by nu test support::prelude:: . Do not import the old macros for new tests: // Bad use nu test support::nu; use nu test support::{nu, nu repl code}; use nu test support::nu with plugins; Why: even when nu is still re-exported by compatibility modules or the prelude on some branches, that is not permission to use it in new tests. The direction is to remove these macros from tests. For a single snippet and a single expected value, chain test .run ... .expect value eq ... : php use nu test support::prelude:: ; test fn can average range - Result { test .run "0..5 | math avg" .expect value eq 2.5 } This is better than a nu test because it compares a real Value converted through IntoValue , not collapsed stdout text. Do not write this: js use nu test support::nu; test fn can average range { let actual = nu "0..5 | math avg" ; assert eq actual.out, "2.5" ; } Use a local variable named code only for long or multiline snippets: use nu test support::prelude:: ; test fn format filesize respects float precision for fractional values - Result { let code = " $env.config = $env.config | upsert float precision 5 1024B | format filesize kB "; test .run code .expect value eq "1.02400 kB" } Guidelines for multiline snippets: Use let code = "..." or let code = r "..." when the snippet is long or multiline. For short snippets, pass a one-line string directly to run ... or run with data ... . Keep Nushell code readable; do not compress everything into one line unless it is genuinely clearer. If the expected output is a multiline Rust string, prefer indoc::indoc . Use cwd on the tester when the snippet reads files relative to a fixture directory: php use nu test support::prelude:: ; test fn reads cargo sample - Result { let code = r " open cargo sample.toml | get package | format pattern "{name} has license {license}" " ; test .cwd "tests/fixtures/formats" .run code .expect value eq "nu has license ISC" } cwd accepts repo-relative paths and sandbox paths. Prefer it over cd in the snippet when the test setup itself should start in a specific directory. run extracts the result into any type implementing FromValue . expect value eq accepts any type implementing IntoValue . Never write run::