<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Reamid</title>
    <description>The latest articles on DEV Community by Reamid (@embers-of-the-fire).</description>
    <link>https://dev.to/embers-of-the-fire</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2707758%2Fe3a6b198-a4b2-48bd-9a04-b134dc481b3a.png</url>
      <title>DEV Community: Reamid</title>
      <link>https://dev.to/embers-of-the-fire</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/embers-of-the-fire"/>
    <language>en</language>
    <item>
      <title>How to Build and Test a Cargo Subcommand</title>
      <dc:creator>Reamid</dc:creator>
      <pubDate>Wed, 30 Sep 2026 08:34:47 +0000</pubDate>
      <link>https://dev.to/embers-of-the-fire/how-to-build-and-test-a-cargo-subcommand-4614</link>
      <guid>https://dev.to/embers-of-the-fire/how-to-build-and-test-a-cargo-subcommand-4614</guid>
      <description>&lt;p&gt;Cargo has a quiet superpower: &lt;code&gt;cargo &amp;lt;anything&amp;gt;&lt;/code&gt; is an extension point. Put a binary named &lt;code&gt;cargo-foo&lt;/code&gt; on &lt;code&gt;PATH&lt;/code&gt; and &lt;code&gt;cargo foo&lt;/code&gt; works — that's how &lt;code&gt;cargo-edit&lt;/code&gt;, &lt;code&gt;cargo-expand&lt;/code&gt;, and dozens of other tools exist.&lt;/p&gt;

&lt;p&gt;In this tutorial we'll walk through what it takes to build one of these subcommands so it &lt;em&gt;feels&lt;/em&gt; like part of Cargo, and — the part most tutorials skip — how to test it when your tool's job is to shell out to Cargo, compile code, and hit a package registry. Our running example is &lt;a href="https://github.com/Embers-of-the-Fire/cargo-dlx" rel="noopener noreferrer"&gt;&lt;code&gt;cargo-dlx&lt;/code&gt;&lt;/a&gt;, a real subcommand that downloads, compiles, and runs a Rust binary without permanently installing it (think &lt;code&gt;npx&lt;/code&gt; or &lt;code&gt;uvx&lt;/code&gt; for Rust):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo dlx ripgrep@14.1.1 &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything below is taken from its actual source, so you can follow along in the repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: The subcommand contract
&lt;/h2&gt;

&lt;p&gt;The mechanics are simple, but there are three details that separate a toy from a tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strip the subcommand name from argv
&lt;/h3&gt;

&lt;p&gt;When a user runs &lt;code&gt;cargo dlx ripgrep&lt;/code&gt;, Cargo invokes your binary with argv:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"cargo-dlx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dlx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ripgrep"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That leading &lt;code&gt;"dlx"&lt;/code&gt; is a convention inherited from git's subcommands. If you feed argv straight into clap, &lt;code&gt;dlx&lt;/code&gt; gets parsed as your first positional argument and everything breaks. You need to strip it — and while you're at it, keep the binary working when invoked directly as &lt;code&gt;cargo-dlx ripgrep&lt;/code&gt; (no prefix). cargo-dlx does this with a &lt;code&gt;Cli::normalize_raw_args&lt;/code&gt; helper called before &lt;code&gt;Cli::parse_from&lt;/code&gt;, and pins the behavior with a test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cargo_test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;strips_cargo_subcommand_prefix&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;project&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="nf"&gt;.cargo_dlx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dlx ripgrep@v14.1.1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// note the "dlx" prefix, as cargo would pass it&lt;/span&gt;
        &lt;span class="nf"&gt;.env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CARGO"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cargo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.with_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.with_stderr_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;str!&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;r#"
[ERROR] the version provided, `v14.1.1` is not a valid SemVer requirement

[HELP] try changing the version to `14.1.1`

"#&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
        &lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Respect &lt;code&gt;$CARGO&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Your subcommand will often need to call back into Cargo itself (cargo-dlx shells out to &lt;code&gt;cargo install&lt;/code&gt;). When it does, use the &lt;code&gt;CARGO&lt;/code&gt; environment variable if set, and fall back to &lt;code&gt;cargo&lt;/code&gt; on &lt;code&gt;PATH&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;cargo-dlx&lt;/code&gt; invokes the Cargo executable from &lt;code&gt;$CARGO&lt;/code&gt; when set, otherwise &lt;code&gt;cargo&lt;/code&gt; from &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This matters more than it looks: rustup proxies, custom toolchains, and your own tests all rely on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exit codes and stderr are interface, not decoration
&lt;/h3&gt;

&lt;p&gt;Propagate the child process's exit code so your subcommand composes in scripts. cargo-dlx's &lt;code&gt;main&lt;/code&gt; ends with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;cargo_dlx&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;cargo_dlx&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Completed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;cargo_dlx&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Execution&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ChildExited&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error: {error}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="nf"&gt;.exit_code&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, &lt;code&gt;cargo dlx some-linter &amp;amp;&amp;amp; echo clean&lt;/code&gt; behaves exactly like the underlying tool would. Diagnostics go to stderr, results go to stdout — the usual rules, but worth stating because a subcommand that violates them breaks every pipeline it's used in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Design to fit in
&lt;/h2&gt;

&lt;p&gt;Anyone can parse args. The harder problem is deciding what your flags, syntax, and error messages &lt;em&gt;should&lt;/em&gt; be. cargo-dlx's approach is worth stealing: its &lt;code&gt;DESIGN.md&lt;/code&gt; states up front&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a polyfill for what could be merged into Cargo. Design decisions should align with existing design elements in Cargo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You don't have to aim for upstreaming, but the underlying principle generalizes: &lt;strong&gt;when a design element already exists in Cargo, reuse it; only invent when you must.&lt;/strong&gt; Concretely, in cargo-dlx:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Package selection&lt;/strong&gt; uses Cargo's &lt;a href="https://doc.rust-lang.org/cargo/reference/pkgid-spec.html" rel="noopener noreferrer"&gt;Package ID Specification&lt;/a&gt; syntax rather than a bespoke one:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  cargo dlx ripgrep@14.1.1
  cargo dlx &lt;span class="s1"&gt;'git+https://github.com/owner/repo.git?rev=&amp;lt;sha&amp;gt;#my-tool'&lt;/span&gt;
  cargo dlx &lt;span class="s1"&gt;'sparse+https://index.crates.io/#ripgrep@14.1.1'&lt;/span&gt;
  cargo dlx &lt;span class="s1"&gt;'path+file:///absolute/path/to/my-tool#my-tool@0.1.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flags keep their Cargo names and meanings&lt;/strong&gt;: &lt;code&gt;--bin&lt;/code&gt;, &lt;code&gt;--example&lt;/code&gt;, &lt;code&gt;--profile&lt;/code&gt;, &lt;code&gt;--features&lt;/code&gt;, &lt;code&gt;--locked&lt;/code&gt;, &lt;code&gt;--offline&lt;/code&gt;, &lt;code&gt;--frozen&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-binary packages follow &lt;code&gt;cargo run&lt;/code&gt; semantics&lt;/strong&gt;: a single &lt;code&gt;[[bin]]&lt;/code&gt; is the default; &lt;code&gt;default-run&lt;/code&gt; resolves ambiguity; otherwise error and list the available binaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The other thing worth copying is the &lt;em&gt;documentation habit&lt;/em&gt;. cargo-dlx's &lt;code&gt;DESIGN.md&lt;/code&gt; records not just decisions but &lt;strong&gt;alternatives considered and prior art&lt;/strong&gt; (&lt;code&gt;yarn dlx&lt;/code&gt;, &lt;code&gt;pnpm dlx&lt;/code&gt;, &lt;code&gt;npm exec&lt;/code&gt;, &lt;code&gt;bunx&lt;/code&gt;, &lt;code&gt;pipx run&lt;/code&gt;, &lt;code&gt;uvx&lt;/code&gt;, &lt;code&gt;deno run&lt;/code&gt;), plus a list of open questions. For a CLI, the design doc is where you answer "why does it work this way?" once, instead of in every issue thread.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Test it with Cargo's own harness
&lt;/h2&gt;

&lt;p&gt;Here's where most subcommand projects give up and write shell scripts. Testing a tool that compiles and runs other Rust code means you need temp projects, a package source, a compiler, and output assertions — per test.&lt;/p&gt;

&lt;p&gt;The trick: &lt;strong&gt;Cargo's own testsuite infrastructure is published as crates&lt;/strong&gt;, and you can use it from any project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dev-dependencies]&lt;/span&gt;
&lt;span class="py"&gt;cargo-test-macro&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.4.9"&lt;/span&gt;
&lt;span class="py"&gt;cargo-test-support&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.10.0"&lt;/span&gt;
&lt;span class="py"&gt;cargo-util&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.2.27"&lt;/span&gt;
&lt;span class="py"&gt;snapbox&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.6.4"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same harness that runs Cargo's own thousands of integration tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  A hermetic Cargo universe per test
&lt;/h3&gt;

&lt;p&gt;Annotate tests with &lt;code&gt;#[cargo_test]&lt;/code&gt; and every test gets an isolated environment: a temp project directory, a fake &lt;code&gt;CARGO_HOME&lt;/code&gt;, and — critically — a &lt;strong&gt;local test registry&lt;/strong&gt;, so you never touch crates.io. Publishing a fixture crate is three lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cargo_test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;runs_registry_binary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dlx-hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/main.rs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;r#"
fn main() {
    println!("hello from cargo-dlx");
}
"#&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.publish&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// publishes to the in-test registry&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;project&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="nf"&gt;.cargo_dlx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dlx-hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.with_stdout_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;str!&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;r#"
hello from cargo-dlx

"#&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
        &lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cargo dlx&lt;/code&gt; then resolves, downloads, compiles, and runs that crate with zero network access. Other source kinds get their own fixtures: &lt;code&gt;git::new_repo(...)&lt;/code&gt; builds a real local git repo for &lt;code&gt;git+&lt;/code&gt; specs, and a second &lt;code&gt;project().at("...")&lt;/code&gt; gives you a path source for &lt;code&gt;file://&lt;/code&gt; specs. Whatever your subcommand consumes, there's a constructor for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wire up a tiny helper
&lt;/h3&gt;

&lt;p&gt;The ergonomic core is a small extension trait over &lt;code&gt;cargo_test_support::Project&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;ProjectExt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/// Creates an `Execs` instance to run the `cargo-dlx` binary&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;cargo_dlx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Execs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="cd"&gt;/// Creates an `Execs` instance to run the globally installed `cargo` command&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;cargo_global&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Execs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the second method: it runs &lt;em&gt;real Cargo&lt;/em&gt; inside the same fixture. That's useful whenever you want to compare your subcommand's behavior against Cargo's own on identical input — for example, checking that your multi-binary error condition matches what &lt;code&gt;cargo run&lt;/code&gt; does with the same project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What does cargo run do with this fixture?&lt;/span&gt;
&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="nf"&gt;.cargo_global&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.with_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.with_stderr_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;str!&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;r#"
[ERROR] `cargo run` could not determine which binary to run. Use the `--bin` option to specify a binary, or the `default-run` manifest key.
available binaries: goodbye, hello

"#&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
    &lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// And does the subcommand fail on the same condition?&lt;/span&gt;
&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="nf"&gt;.cargo_dlx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path+{}#dlx-path-source"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="nf"&gt;.root&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_url&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="nf"&gt;.with_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.with_stderr_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;str!&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;r#"
...
[ERROR] `cargo run` could not determine which binary to run
[HELP] specify the binary with `--bin` option
available binaries: goodbye, hello

"#&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
    &lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Snapshot the output, wildcards included
&lt;/h3&gt;

&lt;p&gt;Assertions come from &lt;a href="https://crates.io/crates/snapbox" rel="noopener noreferrer"&gt;&lt;code&gt;snapbox&lt;/code&gt;&lt;/a&gt;: &lt;code&gt;.with_status(42)&lt;/code&gt;, &lt;code&gt;.with_stdout_data(...)&lt;/code&gt;, &lt;code&gt;.env(...)&lt;/code&gt;, &lt;code&gt;.run()&lt;/code&gt;. The &lt;code&gt;str![[...]]&lt;/code&gt; macro lets you write expected output as a literal block, and &lt;code&gt;...&lt;/code&gt; acts as a line wildcard — essential when Cargo's own build chatter appears interleaved with the output you care about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="nf"&gt;.cargo_dlx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dlx-exit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.with_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.with_stderr_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;str!&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s"&gt;r#"
...
dlx-exit failed intentionally

"#&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
    &lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For whole-UI surfaces like &lt;code&gt;--help&lt;/code&gt;, &lt;code&gt;cargo-test-support&lt;/code&gt; ships &lt;code&gt;compare::assert_ui()&lt;/code&gt;, which snapshots the full styled terminal output — colors, hyperlinks and all — against a checked-in &lt;code&gt;stdout.term.svg&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cargo_test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;case&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;snapbox&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;cargo_ui&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--help"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.assert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.success&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.stdout_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;file!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"stdout.term.svg"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;.stderr_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;str!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your clap definitions change, &lt;code&gt;SNAPSHOTS=overwrite cargo test&lt;/code&gt; regenerates the snapshot, and the rendered diff shows up in code review like any other change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Test the side effects, not just the output
&lt;/h2&gt;

&lt;p&gt;A subcommand's real footprint is often invisible in the terminal: temp directories, caches, lock files. cargo-dlx installs binaries into a temporary root, caches build artifacts under &lt;code&gt;~/.cargo-dlx/build&lt;/code&gt;, and offers &lt;code&gt;--clear&lt;/code&gt; to clean up. Roughly half its testsuite is dedicated to proving that machinery works: every combination of &lt;code&gt;CARGO_DLX_ROOT&lt;/code&gt; / &lt;code&gt;CARGO_DLX_TEMP&lt;/code&gt; / &lt;code&gt;CARGO_DLX_BUILD&lt;/code&gt; / &lt;code&gt;--cache-dir&lt;/code&gt;, including the hostile case where &lt;code&gt;$HOME&lt;/code&gt; doesn't exist at all.&lt;/p&gt;

&lt;p&gt;The assertions are plain filesystem checks after the command runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;temp_dir_is_empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;target_cache_dir_names&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rule of thumb: &lt;strong&gt;stdout proves the happy path; the directory tree proves the cleanup.&lt;/strong&gt; If your tool creates, caches, or deletes files, assert on the files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Building a cargo subcommand that holds up in the real world comes down to four habits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Honor the contract&lt;/strong&gt; — strip the subcommand-name argument, respect &lt;code&gt;$CARGO&lt;/code&gt;, propagate exit codes, keep diagnostics on stderr.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reuse Cargo's design vocabulary&lt;/strong&gt; — pkgid-specs, flag names, and semantics your users already know; write down the decisions and alternatives in a &lt;code&gt;DESIGN.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with Cargo's own harness&lt;/strong&gt; — &lt;code&gt;cargo-test-support&lt;/code&gt;, &lt;code&gt;cargo-test-macro&lt;/code&gt;, and &lt;code&gt;snapbox&lt;/code&gt; give you hermetic projects, a local registry, git/path fixtures, and snapshot assertions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assert on side effects&lt;/strong&gt; — caches, temp dirs, and cleanup logic deserve the same rigor as stdout.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All the code in this post is from &lt;a href="https://github.com/Embers-of-the-Fire/cargo-dlx" rel="noopener noreferrer"&gt;&lt;code&gt;cargo-dlx&lt;/code&gt;&lt;/a&gt; (&lt;code&gt;cargo install cargo-dlx&lt;/code&gt; if you want to try it). Its &lt;code&gt;tests/testsuite/&lt;/code&gt; directory doubles as a worked example of everything above.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>rust</category>
      <category>cargo</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
