[−][src]Crate cli_test_dir
This crate makes it easier to write integration tests for CLI applications. It's based on the "workdir" pattern used by BurntSushi's xsv and ripgrep crates, but packaged in an easy-to-reuse form.
To use this crate, add the following lines to your Cargo.toml
file:
[dev-dependencies]
# You can replace "*" with the current version of this crate.
cli_test_dir = "*"
Then add the following to the top of tests/tests.rs
:
extern crate cli_test_dir;
You should now be able to write tests as follows:
use cli_test_dir::*; #[test] fn write_output_file() { let testdir = TestDir::new("myapp", "write_output_file"); testdir.cmd() .arg("out.txt") .expect_success(); testdir.expect_path("out.txt"); }
You can use any options from std::process::Command
to invoke
your program.
Testing that the program ran successfully
To check that a command succeeds, we can write:
let testdir = TestDir::new("true", "true_succeeds"); let mut cmd = testdir.cmd(); cmd.expect_success();
But this test would fail:
// Fails. let testdir = TestDir::new("false", "false_succeeds"); let testdir = TestDir::new("cmd", "false_succeeds"); let mut cmd = testdir.cmd(); cmd.expect_success();
Testing that the program exited with an error.
Sometimes you want to test that a program fails to run successfully.
let testdir = TestDir::new("false", "false_fails"); let mut cmd = testdir.cmd(); cmd.expect_failure();
And as you would expect, this test would fail:
// Fails. let testdir = TestDir::new("true", "true_fails"); let mut cmd = testdir.cmd(); cmd.expect_failure();
File input and output
The src_path
function can be used to build paths relative to the
top-level of our crate, and expect_path
can be used to make sure an
output file exists:
let testdir = TestDir::new("cp", "cp_copies_files"); let mut cmd = testdir.cmd(); cmd .arg(testdir.src_path("fixtures/input.txt")) .arg("output.txt") .expect_success(); testdir.expect_path("output.txt");
We can also create the input file manually or look for specific contents in the output file if we wish:
let testdir = TestDir::new("cp", "cp_copies_files_2"); let mut cmd = testdir.cmd(); testdir.create_file("input.txt", "Hello, world!\n"); cmd .arg("input.txt") .arg("output.txt") .expect_success(); testdir.expect_contains("output.txt", "Hello"); testdir.expect_file_contents("output.txt", "Hello, world!\n");
There are also negative versions of these functions where useful:
let testdir = TestDir::new("cp", "negative_tests"); let mut cmd = testdir.cmd(); testdir.create_file("input.txt", "Hello, world!\n"); cmd .arg("input.txt") .arg("output.txt") .expect_success(); testdir.expect_does_not_contain("output.txt", "Goodbye"); testdir.expect_no_such_path("does_not_exist.txt");
Standard input and output
We can also test standard input and output:
let testdir = TestDir::new("cat", "cat_passes_data_through"); let mut cmd = testdir.cmd(); let output = cmd .output_with_stdin("Hello\n") .expect_success(); assert_eq!(output.stdout_str(), "Hello\n");
If you wish, you can display a command's output using tee_output
:
let testdir = TestDir::new("cat", "tee_output_shows_output"); let mut cmd = testdir.cmd(); let output = cmd .output_with_stdin("Hello\n") // Show `stdout` and `stderr`. .tee_output() .expect_success(); assert_eq!(output.stdout_str(), "Hello\n");
Note that this will currently print out all of stdout
first, then all of
stderr
, instead of interleaving them normally.
To see the output of tee_output
, you will also need to invoke cargo
as
follows:
cargo test -- --nocapture
Contributing
Your feedback and contributions are welcome! Please see GitHub for details.
Structs
TestDir | This code is inspired by the |
Traits
CommandExt | Extension methods for |
ExpectStatus | We define |
OutputExt | Extension methods for |
TeeOutputExt | Display command output and return it for examination. |