CLI Tool or Library

A binary or package consumed by other developers. The public interface is the CLI invocation surface or the library’s exported API. Brief sketch.

A binary (CLI) or package (library) consumed by other developers. The “public interface” is the CLI invocation surface (argv, stdin, stdout, stderr, exit code) or the library’s exported API.

The pattern is different because the consumer is a developer or another program, not a user clicking a button. Cross-platform behavior, semantic versioning, and backward compatibility matter more than they do for a service.

What needs covered

LayerConcernTest type
Pure logicFunctions, classes, parsersSolitary unit tests
CLI invocationArgument parsing, exit codes, output streamsComponent tests through the CLI entrypoint
Cross-platformPath separators, line endings, signal handlingCross-OS test matrix running the suite on every supported OS in CI
Public API surfaceLibrary’s exported types and functionsAPI surface tests (snapshot of the public API; diff fails the build)
Documented examplesThe README examples actually workDoctests / executable docs
CLI Tool or Library: layers and the tests that cover eachFive architectural layers stacked top to bottom. The first four (pure logic and parsing, CLI invocation surface or library API, file system and subprocess adapter, and documented README examples) are inside the component boundary. Below the dashed component boundary, the real OS, file system, and subprocess are drawn with a dashed border. Each band shows its name, a one-line description, and the test types that exercise it as small coloured pills. Solitary unit tests cover pure logic and parsing. Component tests cover invocation through the entrypoint. Adapter integration tests cover the file system and subprocess against the real OS in a temp directory. The API surface diff catches removal or rename of any public symbol. Doctests verify README examples run against the real binary or library. The cross-OS CI matrix runs the suite on every supported OS to catch platform-specific bugs.CLI Tool or Library: Layers and the Tests That Cover EachINSIDE THE COMPONENT BOUNDARYPure logic and parsingSolitary unitComponentFunctions, classes, parsers; no I/OCLI invocation surface or library APIComponentAPI surface diffDoctestsCross-OS CIargv, stdin, stdout, stderr, exit code, --help, exported symbolsFile system and subprocess adapterComponentAdapter integ.Cross-OSPaths, encodings, signal handling, spawn semanticsDocumented README examplesDoctestsExamples in the docs actually run against the real binary or librarycomponent boundaryOUTSIDE THE BOUNDARYReal OS, file system, subprocessComponentAdapter integ.Cross-OSPath separators, line endings, signals. Doubled in component; real in adapter integration and the cross-OS matrix.internal layerreal code under testexternal (dashed border)doubled in this test
Layered diagram of a CLI tool or library with five architectural layers. The first four (pure logic and parsing, CLI invocation surface or library API, file system and subprocess adapter, documented README examples) are inside the component boundary. Below the dashed boundary, the real OS, file system, and subprocess are drawn with a dashed border. Solitary unit tests cover pure logic and parsing. Component tests cover invocation through the entrypoint. Adapter integration tests cover the file system and subprocess against the real OS in a temp directory. The API surface diff catches removal or rename of any public symbol. Doctests verify README examples run against the real binary or library. The cross-OS CI matrix runs the suite on every supported OS to catch platform-specific bugs.

Positive test cases

Common cases to consider, not an exhaustive list. Drop items that don’t apply and add ones the pattern doesn’t mention but your component needs.

  • Valid arguments: produce documented stdout output, no stderr, and exit code 0.
  • Pipe-friendly mode: produces machine-readable output (JSON/NDJSON) when stdout is not a TTY.
  • Library API: returns documented values for valid input.

Negative test cases

Common cases to consider, not an exhaustive list. Drop items that don’t apply and add ones the pattern doesn’t mention but your component needs.

  • Bad arguments: exit with the documented non-zero code and structured stderr.
  • Help text: reachable via --help.
  • Large input: does not OOM.
  • Interrupt (Ctrl-C, SIGTERM): runs cleanup and flushes or rolls back partial output.
  • Invalid arguments to the library: throws the documented error type.
  • Public symbol removed or renamed: the API-surface test fails the build.

Test double validation

File system doubles validated by integration tests against the real FS in a temp directory. Subprocess doubles validated by tests that actually spawn the subprocess on each supported OS. Doctests validate README examples against the real binary or library on every build.

Pipeline placement

Unit and component tests run in CI Stage 1 on every supported OS; API surface diff and doctests in CI Stage 1; cross-platform integration tests in CI Stage 2 if slow.