Controlling How Tests Are Run

Just as cargo run compiles and runs your code, cargo test compiles your code in test mode and runs the resulting test binary. The default behavior is to run all tests in parallel and capture output. You can customize this behavior with command line options.

Command Line Options

Some options go to cargo test, and some go to the test binary. To separate them, use --:

cargo test --help        # Options for cargo test
cargo test -- --help     # Options for the test binary

Running Tests in Parallel or Consecutively

By default, tests run in parallel using threads. This makes tests complete faster but means they should not depend on each other or share mutable state.

If your tests depend on shared state, run them consecutively:

cargo test -- --test-threads=1

This sets the number of test threads to 1, running tests one at a time.

Showing Function Output

By default, successful tests capture any println! output. If a test passes, you won't see its printed output. Only failing tests show their output.

To see output from passing tests:

cargo test -- --show-output

Example:

fn printsAndReturnsValue(a: Int): Int {
    println!("I got the value \(a)")
    a
}

#[cfg(test)]
module tests {
    import super.*

    #[test]
    fn thisPasses() {
        let value = printsAndReturnsValue(4)
        assertEq!(value, 4)
    }

    #[test]
    fn thisFails() {
        let value = printsAndReturnsValue(8)
        assertEq!(value, 5)
    }
}

Running cargo test:

running 2 tests
test tests.thisPasses ... ok
test tests.thisFails ... FAILED

failures:

---- tests.thisFails stdout ----
I got the value 8
thread 'tests.thisFails' panicked at src/lib.ox:17:9:
assertion `left == right` failed
  left: 8
 right: 5

Notice only the failing test shows its println! output.

Running cargo test -- --show-output:

running 2 tests
test tests.thisFails ... FAILED
test tests.thisPasses ... ok

successes:

---- tests.thisPasses stdout ----
I got the value 4


successes:
    tests.thisPasses

failures:

---- tests.thisFails stdout ----
I got the value 8
thread 'tests.thisFails' panicked at src/lib.ox:17:9:
assertion `left == right` failed
  left: 8
 right: 5

Now both tests show their output.

Running a Subset of Tests by Name

Running the full test suite takes time. You can run specific tests by name.

Running Single Tests

Pass the test name to cargo test:

cargo test thisPasses

Output:

running 1 test
test tests.thisPasses ... ok

test result: ok. 1 passed; 0 filtered out

Note: You can use either the Oxide camelCase name (thisPasses) or the Rust snake_case name (this_passes).

Filtering to Run Multiple Tests

Specify part of a test name to run all matching tests:

public fn addTwo(a: Int): Int {
    a + 2
}

#[cfg(test)]
module tests {
    import super.*

    #[test]
    fn addTwoAndTwo() {
        assertEq!(4, addTwo(2))
    }

    #[test]
    fn addThreeAndTwo() {
        assertEq!(5, addTwo(3))
    }

    #[test]
    fn oneHundred() {
        assertEq!(102, addTwo(100))
    }
}

Running all tests:

cargo test
running 3 tests
test tests.addThreeAndTwo ... ok
test tests.addTwoAndTwo ... ok
test tests.oneHundred ... ok

Running only the "add" tests:

cargo test add
running 2 tests
test tests.addThreeAndTwo ... ok
test tests.addTwoAndTwo ... ok

Running Tests by Module Name

You can also filter by module name:

cargo test tests::

This runs all tests in the tests module.

Ignoring Tests Unless Specifically Requested

Some tests are expensive and you want to skip them in normal runs. Use the #[ignore] attribute:

#[cfg(test)]
module tests {
    #[test]
    fn itWorks() {
        assertEq!(2 + 2, 4)
    }

    #[test]
    #[ignore]
    fn expensiveTest() {
        // This test takes a long time to run
        // Code that takes an hour to run...
    }
}

Running cargo test:

running 2 tests
test tests.expensiveTest ... ignored
test tests.itWorks ... ok

test result: ok. 1 passed; 0 failed; 1 ignored

To run only the ignored tests:

cargo test -- --ignored

To run all tests including ignored ones:

cargo test -- --include-ignored

Running Tests by Type

Cargo can run different types of tests:

cargo test --lib          # Run library tests only
cargo test --doc          # Run documentation tests only
cargo test --bins         # Run binary tests only
cargo test --tests        # Run integration tests only

Running a Specific Test File

For integration tests, run tests from a specific file:

cargo test --test integration_test

This runs tests only from tests/integration_test.rs (or tests/integration_test.ox).

Useful Test Options

Here are commonly used test options:

OptionDescription
--test-threads=NNumber of parallel test threads
--show-outputShow output from passing tests
--ignoredRun only ignored tests
--include-ignoredRun all tests including ignored
--nocaptureDon't capture output (same as --show-output)
--test NAMERun a specific test binary
--exactMatch test name exactly
--skip PATTERNSkip tests matching pattern

Exact Matching

By default, test name matching is a substring match. For exact matching:

cargo test thisPasses -- --exact

Skipping Tests

Skip tests matching a pattern:

cargo test -- --skip expensive

Summary

  • Use cargo test to run all tests
  • Add -- to pass options to the test binary
  • Control parallelism with --test-threads
  • See output with --show-output
  • Filter tests by name with cargo test <pattern>
  • Use #[ignore] for expensive tests
  • Run ignored tests with --ignored
  • Use --exact for exact name matching
  • Skip patterns with --skip

The next section covers how to organize your tests into unit tests and integration tests.