Introduction
The cargo test --no-run flag compiles test binaries without executing them, which is useful for CI pipelines that separate build and test stages. However, finding and running the resulting test binary is non-trivial because cargo generates complex filenames that include the test target name, hash, and platform information.
Symptoms
cargo test --no-runsucceeds but test binary cannot be found- CI pipeline builds tests but test stage cannot locate the binary
- Running the binary directly produces
no tests to runor unexpected behavior - Test binary names change between builds, breaking cached paths
- Integration tests and unit tests produce separate binaries
Example workflow that fails: ```bash # Build works cargo test --no-run --release
# But finding the binary is unclear ./target/release/deps/my_test-* # Glob may match multiple files ```
Common Causes
- Test binary names include content hashes that change each build
- Multiple test targets produce multiple binaries
--releaseand debug binaries go to different directories- Cargo workspace layout changes binary output paths
CARGO_TARGET_DIRenvironment variable redirects output
Step-by-Step Fix
- 1.**Use
cargo test --no-run --message-format=jsonto find binaries**: - 2.```bash
- 3.# Get machine-readable output with binary paths
- 4.cargo test --no-run --message-format=json 2>&1 | \
- 5.jq -r 'select(.reason == "compiler-artifact" and .profile.test == true) | .executable'
- 6.
` - 7.List test binaries with cargo metadata:
- 8.```bash
- 9.# Get test executable paths
- 10.cargo test --no-run -v 2>&1 | grep "Running"
# Or use cargo's built-in listing cargo test --no-run -- --list 2>/dev/null || true ```
- 1.Run specific test binary directly:
- 2.```bash
- 3.# Find and run the test binary
- 4.TEST_BIN=$(cargo test --no-run --message-format=json 2>/dev/null | \
- 5.jq -r 'select(.reason == "compiler-artifact" and .target.name == "my_test") | .executable')
# Run with filters $TEST_BIN --test-threads=1 specific_test_name $TEST_BIN --list # List all tests $TEST_BIN --show-output # Show stdout from tests ```
- 1.**Use a stable output path with
CARGO_TARGET_DIR**: - 2.```bash
- 3.# Set a known target directory
- 4.export CARGO_TARGET_DIR=$(pwd)/target
cargo test --no-run
# Find binaries in a predictable location ls target/debug/deps/*-*.exe # Windows ls target/debug/deps/*-* # Unix ```
- 1.CI-friendly build and test separation:
- 2.```bash
- 3.#!/bin/bash
- 4.# build.sh
- 5.cargo test --no-run --message-format=json > /tmp/build-artifacts.json
# test.sh (runs later) while read -r binary; do echo "Running: $binary" $binary --test-threads=$(nproc) done < <(jq -r 'select(.reason == "compiler-artifact" and .profile.test == true and .executable != null) | .executable' /tmp/build-artifacts.json) ```
Prevention
- Use
--message-format=jsonin CI for reliable binary path discovery - Cache the
target/directory in CI to avoid recompilation between stages - Pin test binary paths using
cargo metadatain your CI scripts - Consider using
cargo-nextestfor better test execution:cargo nextest run - Use
--testflag to build specific test targets:cargo test --no-run --test integration - Document the test binary discovery process in your CI pipeline README