assert_cmd: testing a Rust CLI by running the binary you just built
Assert process::Command - Easy command initialization and assertions
At a glance
- What is it?
- assert_cmd gives a Rust integration test a Command wrapper that finds the crate's own binary and lets you assert on exit status, stdout and stderr, so you stop shelling out to std::process::Command and hand-rolling output matching. The remaining sharp edge is that it asserts on the built artefact, so the test only means something after a build.
- Who is it for?
- Adopt assert_cmd when you ship a Rust command line tool and want integration tests that run the real binary and assert on its output, since it removes the boilerplate of locating and matching on the process result. Do not adopt it for unit tests of internal functions, which have no process to run.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 19 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What it actually saves you
assert_cmd aims to simplify the process for doing integration testing of CLIs, and it names the two things it removes.
The first is finding your crate's binary to test. When you write an integration test for a Rust binary, the test does not link against your crate's code, it runs a separate executable. Locating that executable, which lives in the target directory under a name derived from the package and the binary name, and getting the path right on every platform, is a chore that every Rust CLI project otherwise repeats.
The second is asserting on the result of your program's run. With std::process::Command you get a std::process::Output back, and the assertion work is yours: the status code check, the stdout comparison, the stderr comparison, and the failure messages that tell a developer what actually happened. assert_cmd provides a fluent layer over that.
The result is a small crate with a small surface. The description in the repository is Assert process::Command - Easy command initialization and assertions, and the whole public idea is in that one sentence.
The primary language is Rust, the licence is Apache-2.0 or MIT at your option, the default branch is master, and the documentation lives at https://docs.rs/assert_cmd. The last push was on 2026-10-04, the repository is not archived, and there are no GitHub releases, which is normal because the crate is versioned on crates.io rather than through repository tags.
The example, and what it tells you about the API
The README gives a trivial example, and it is worth reading closely because the shape of the API is the whole point.
use assert_cmd::Command;
let mut cmd = Command::cargo_bin("bin_fixture").unwrap();
cmd.assert().success();Three things happen in three lines. Command is imported from assert_cmd rather than from std::process, which is what makes the rest possible. Command::cargo_bin takes the name of your binary and locates it, returning a Result that is unwrapped here. And the mutable command is finished with assert, which returns an assertion object on which you call success.
The chaining shape is the ergonomic point. assert() consumes the command and gives you something you can call success on, and the same object has other methods for output predicates, so a test that checks the exit code and then checks stdout reads as a sequence of calls on one value rather than a sequence of separate helper functions with awkward ownership between them.
The fact that the example is marked no_run is a detail worth noticing. It is not a test of the crate itself, it is a usage example, and the marker means it is compiled as part of the documentation test suite but not run. The examples/ directory in the repository holds the same idea in fuller form, with example_fixture.rs providing a binary to run and failure.rs demonstrating a failing case.
The neighbouring crates, and the shape of the Rust testing stack
The README lists other crates that might be useful when testing command line programs, and the list is a map of the problem space around this one.
escargot is for more control over configuring the crate's binary, which is the piece assert_cmd automates for you. duct is for orchestrating multiple processes, so it covers the case where your CLI is a pipeline rather than a single command, and commandspec is offered as an easier way of writing those commands. rexpect is for testing interactive programs, which is the gap assert_cmd itself does not fill because it is built around non-interactive runs with captured output.
Then there is the filesystem side. assert_fs provides filesystem fixtures and assertions, or tempfile for scratchpad directories. dir-diff is for testing file side-effects, which is a common need for a CLI that writes output. And cross is for cross-platform testing.
Read together, the list draws a boundary around assert_cmd. It handles one process, run non-interactively, with output captured and asserted. Everything adjacent to that, process orchestration, interactivity, filesystem state, platform variation, is somebody else's crate. That division is a sign of a well-scoped library rather than an incomplete one.
The dependency situation in the repository tree supports the same picture. The Cargo.toml sets up a workspace with resolver 3, edition 2024 and a rust-version of 1.85 as the MSRV, and it carries a long list of workspace lints, mostly clippy rules set to warn, with a handful explicitly allowed and annotated with the reason. A crate that wants to be a good citizen in a large Rust codebase spends effort on its own lint configuration, and that effort is a reasonable proxy for how the project treats its users' clippy runs.
The build ordering problem, which the crate does not solve for you
There is one thing about assert_cmd that is easy to miss and that determines whether your integration test means anything.
Command::cargo_bin locates an executable that already exists. It does not build your crate. If the binary has not been compiled, cargo_bin will not find it, and the test fails for a reason that has nothing to do with your code.
That means the ordering of your CI steps matters. A build step has to run before the integration tests, and the test invocation has to see the same target directory the build wrote to. The repository tree shows the pieces of a project that takes this seriously: a committed.toml, a release.toml for release automation, a build.rs at the root, a deny.toml for dependency policy checks, and a Cargo.lock that is checked in. There is also a .cargo/ directory, which is where per-project cargo configuration lives, and that is often where a project pins the target directory to keep the build and the test looking in the same place.
The upside of this arrangement is that the test runs the real artefact, the same binary a user would install, rather than some in-process approximation of it. The downside is that the test is only as good as the build that preceded it, and a stale binary produces a passing test of stale behaviour. If you have ever had an integration test pass against code you did not just change, this is the mechanism behind it.
So the practical guidance is to make the build explicit in CI rather than relying on Cargo's implicit behaviour, and to make sure the tests and the build share a target directory. The crate's documentation covers the API; the ordering is yours to get right.
The licence, the contribution rule and the people using it
Two details in the README are worth a look for anyone thinking about depending on this crate.
The licence is dual, at your option: Apache License 2.0, with the text in LICENSE-APACHE, or the MIT license, with the text in LICENSE-MIT. The Cargo.toml workspace package records it as MIT OR Apache-2.0. For a company that has a policy about Apache 2.0, the MIT option is there, and having both is the convention in the Rust ecosystem for a reason.
The contribution rule is the standard Rust dual-licensing statement. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work, as defined in the Apache-2.0 licence, shall be dual-licensed as above without any additional terms or conditions. The effect for a corporate contributor is that an internal contribution does not have to be renegotiated, and the effect for a user is that the project cannot be relicensed away from the dual grant.
The testimonials section is short and specific rather than generic. One is from fitzgen, and another ends with praise for the WG-cli working group, one notes that running commands and dealing with output can be complex in many many ways and that assert_cmd smoothing that improves ergonomics significantly, one says they have used it in other projects and are extremely pleased with it, and one says it pretty much is my testing strategy so far with a small app under test and that it made it really easy to add test coverage even while learning to write Rust.
That last one is the most useful signal in the list, because it describes the case the crate is actually best at: a small project where the CLI is the product and the test suite is the only way to check it end to end.
Editorial conclusion
Adopt assert_cmd when you ship a Rust command line tool and want integration tests that run the real binary and assert on its output, since it removes the boilerplate of locating and matching on the process result. Do not adopt it for unit tests of internal functions, which have no process to run. Before you adopt it, check how your CI builds the binary before the test step, because assert_cmd runs the artefact rather than the source, and note the last push was on 2026-10-04.
Frequently asked questions
What does assert_cmd do in Rust?
It wraps process::Command to simplify integration testing of command line programs, handling the two hard parts: finding your crate's binary to test, and asserting on the result of the program's run.
How do I use assert_cmd to run my binary in a test?
Import Command from assert_cmd, call Command::cargo_bin with your binary name and unwrap the result, then call assert() on the command and use success() on the returned assertion object to check the exit status.
Does assert_cmd build my binary before running the test?
No. Command::cargo_bin locates an executable that already exists, so a build step has to run before the integration tests, and the build and the test have to share a target directory or the test will not find the binary.
What licence is assert_cmd released under?
It is licensed under either of the Apache License 2.0 or the MIT license, at your option, and the Cargo.toml records it as MIT OR Apache-2.0. Contributions are dual-licensed the same way unless the contributor states otherwise.
Which crates are alternatives or companions to assert_cmd?
The README lists escargot for more control over configuring the crate's binary, duct and commandspec for orchestrating multiple processes, rexpect for testing interactive programs, assert_fs and tempfile for filesystem fixtures, dir-diff for testing file side-effects, and cross for cross-platform testing.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/assert-rs-assert-cmd)Was this useful?