feat: added cleanscript
This commit is contained in:
38
ch11/ch11-00-testing.html
Normal file
38
ch11/ch11-00-testing.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Writing Automated Tests</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="writing-automated-tests"><a class="header" href="#writing-automated-tests">Writing Automated Tests</a></h1>
|
||||
<p>In his 1972 essay “The Humble Programmer,” Edsger W. Dijkstra said that “program
|
||||
testing can be a very effective way to show the presence of bugs, but it is
|
||||
hopelessly inadequate for showing their absence.” That doesn’t mean we shouldn’t
|
||||
try to test as much as we can!</p>
|
||||
<p><em>Correctness</em> in our programs is the extent to which our code does what we
|
||||
intend it to do. Rust is designed with a high degree of concern about the
|
||||
correctness of programs, but correctness is complex and not easy to prove.
|
||||
Rust’s type system shoulders a huge part of this burden, but the type system
|
||||
cannot catch everything. As such, Rust includes support for writing automated
|
||||
software tests.</p>
|
||||
<p>Say we write a function <code>add_two</code> that adds 2 to whatever number is passed to
|
||||
it. This function’s signature accepts an integer as a parameter and returns an
|
||||
integer as a result. When we implement and compile that function, Rust does all
|
||||
the type checking and borrow checking that you’ve learned so far to ensure
|
||||
that, for instance, we aren’t passing a <code>String</code> value or an invalid reference
|
||||
to this function. But Rust <em>can’t</em> check that this function will do precisely
|
||||
what we intend, which is return the parameter plus 2 rather than, say, the
|
||||
parameter plus 10 or the parameter minus 50! That’s where tests come in.</p>
|
||||
<p>We can write tests that assert, for example, that when we pass <code>3</code> to the
|
||||
<code>add_two</code> function, the returned value is <code>5</code>. We can run these tests whenever
|
||||
we make changes to our code to make sure any existing correct behavior has not
|
||||
changed.</p>
|
||||
<p>Testing is a complex skill: Although we can’t cover in one chapter every detail
|
||||
about how to write good tests, in this chapter we will discuss the mechanics of
|
||||
Rust’s testing facilities. We’ll talk about the annotations and macros
|
||||
available to you when writing your tests, the default behavior and options
|
||||
provided for running your tests, and how to organize tests into unit tests and
|
||||
integration tests.</p>
|
||||
</body>
|
||||
</html>
|
||||
994
ch11/ch11-01-writing-tests.html
Normal file
994
ch11/ch11-01-writing-tests.html
Normal file
@@ -0,0 +1,994 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>How to Write Tests</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id="how-to-write-tests"><a class="header" href="#how-to-write-tests">How to Write Tests</a></h2>
|
||||
<p><em>Tests</em> are Rust functions that verify that the non-test code is functioning in
|
||||
the expected manner. The bodies of test functions typically perform these three
|
||||
actions:</p>
|
||||
<ul>
|
||||
<li>Set up any needed data or state.</li>
|
||||
<li>Run the code you want to test.</li>
|
||||
<li>Assert that the results are what you expect.</li>
|
||||
</ul>
|
||||
<p>Let’s look at the features Rust provides specifically for writing tests that
|
||||
take these actions, which include the <code>test</code> attribute, a few macros, and the
|
||||
<code>should_panic</code> attribute.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="the-anatomy-of-a-test-function"></a></p>
|
||||
<h3 id="structuring-test-functions"><a class="header" href="#structuring-test-functions">Structuring Test Functions</a></h3>
|
||||
<p>At its simplest, a test in Rust is a function that’s annotated with the <code>test</code>
|
||||
attribute. Attributes are metadata about pieces of Rust code; one example is
|
||||
the <code>derive</code> attribute we used with structs in Chapter 5. To change a function
|
||||
into a test function, add <code>#[test]</code> on the line before <code>fn</code>. When you run your
|
||||
tests with the <code>cargo test</code> command, Rust builds a test runner binary that runs
|
||||
the annotated functions and reports on whether each test function passes or
|
||||
fails.</p>
|
||||
<p>Whenever we make a new library project with Cargo, a test module with a test
|
||||
function in it is automatically generated for us. This module gives you a
|
||||
template for writing your tests so that you don’t have to look up the exact
|
||||
structure and syntax every time you start a new project. You can add as many
|
||||
additional test functions and as many test modules as you want!</p>
|
||||
<p>We’ll explore some aspects of how tests work by experimenting with the template
|
||||
test before we actually test any code. Then, we’ll write some real-world tests
|
||||
that call some code that we’ve written and assert that its behavior is correct.</p>
|
||||
<p>Let’s create a new library project called <code>adder</code> that will add two numbers:</p>
|
||||
<pre><code class="language-console">$ cargo new adder --lib
|
||||
Created library `adder` project
|
||||
$ cd adder
|
||||
</code></pre>
|
||||
<p>The contents of the <em>src/lib.rs</em> file in your <code>adder</code> library should look like
|
||||
Listing 11-1.</p>
|
||||
<figure class="listing" id="listing-11-1">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<!-- manual-regeneration
|
||||
cd listings/ch11-writing-automated-tests
|
||||
rm -rf listing-11-01
|
||||
cargo new listing-11-01 --lib --name adder
|
||||
cd listing-11-01
|
||||
echo "$ cargo test" > output.txt
|
||||
RUSTFLAGS="-A unused_variables -A dead_code" RUST_TEST_THREADS=1 cargo test >> output.txt 2>&1
|
||||
git diff output.txt # commit any relevant changes; discard irrelevant ones
|
||||
cd ../../..
|
||||
-->
|
||||
<pre><code class="language-rust noplayground">pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-1">Listing 11-1</a>: The code generated automatically by <code>cargo new</code></figcaption>
|
||||
</figure>
|
||||
<p>The file starts with an example <code>add</code> function so that we have something to
|
||||
test.</p>
|
||||
<p>For now, let’s focus solely on the <code>it_works</code> function. Note the <code>#[test]</code>
|
||||
annotation: This attribute indicates this is a test function, so the test
|
||||
runner knows to treat this function as a test. We might also have non-test
|
||||
functions in the <code>tests</code> module to help set up common scenarios or perform
|
||||
common operations, so we always need to indicate which functions are tests.</p>
|
||||
<p>The example function body uses the <code>assert_eq!</code> macro to assert that <code>result</code>,
|
||||
which contains the result of calling <code>add</code> with 2 and 2, equals 4. This
|
||||
assertion serves as an example of the format for a typical test. Let’s run it
|
||||
to see that this test passes.</p>
|
||||
<p>The <code>cargo test</code> command runs all tests in our project, as shown in Listing
|
||||
11-2.</p>
|
||||
<figure class="listing" id="listing-11-2">
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.57s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-01ad14159ff659ab)
|
||||
|
||||
running 1 test
|
||||
test tests::it_works ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests adder
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<figcaption><a href="#listing-11-2">Listing 11-2</a>: The output from running the automatically generated test</figcaption>
|
||||
</figure>
|
||||
<p>Cargo compiled and ran the test. We see the line <code>running 1 test</code>. The next
|
||||
line shows the name of the generated test function, called <code>tests::it_works</code>,
|
||||
and that the result of running that test is <code>ok</code>. The overall summary <code>test result: ok.</code> means that all the tests passed, and the portion that reads <code>1 passed; 0 failed</code> totals the number of tests that passed or failed.</p>
|
||||
<p>It’s possible to mark a test as ignored so that it doesn’t run in a particular
|
||||
instance; we’ll cover that in the <a href="ch11-02-running-tests.html#ignoring-tests-unless-specifically-requested">“Ignoring Tests Unless Specifically
|
||||
Requested”</a><!-- ignore --> section later in this chapter. Because we
|
||||
haven’t done that here, the summary shows <code>0 ignored</code>. We can also pass an
|
||||
argument to the <code>cargo test</code> command to run only tests whose name matches a
|
||||
string; this is called <em>filtering</em>, and we’ll cover it in the <a href="ch11-02-running-tests.html#running-a-subset-of-tests-by-name">“Running a
|
||||
Subset of Tests by Name”</a><!-- ignore --> section. Here, we haven’t
|
||||
filtered the tests being run, so the end of the summary shows <code>0 filtered out</code>.</p>
|
||||
<p>The <code>0 measured</code> statistic is for benchmark tests that measure performance.
|
||||
Benchmark tests are, as of this writing, only available in nightly Rust. See
|
||||
<a href="../unstable-book/library-features/test.html">the documentation about benchmark tests</a> to learn more.</p>
|
||||
<p>The next part of the test output starting at <code>Doc-tests adder</code> is for the
|
||||
results of any documentation tests. We don’t have any documentation tests yet,
|
||||
but Rust can compile any code examples that appear in our API documentation.
|
||||
This feature helps keep your docs and your code in sync! We’ll discuss how to
|
||||
write documentation tests in the <a href="../ch14/ch14-02-publishing-to-crates-io.html#documentation-comments-as-tests">“Documentation Comments as
|
||||
Tests”</a><!-- ignore --> section of Chapter 14. For now, we’ll
|
||||
ignore the <code>Doc-tests</code> output.</p>
|
||||
<p>Let’s start to customize the test to our own needs. First, change the name of
|
||||
the <code>it_works</code> function to a different name, such as <code>exploration</code>, like so:</p>
|
||||
<p><span class="filename">Filename: src/lib.rs</span></p>
|
||||
<pre><code class="language-rust noplayground">pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn exploration() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}</code></pre>
|
||||
<p>Then, run <code>cargo test</code> again. The output now shows <code>exploration</code> instead of
|
||||
<code>it_works</code>:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.59s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||||
|
||||
running 1 test
|
||||
test tests::exploration ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests adder
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>Now we’ll add another test, but this time we’ll make a test that fails! Tests
|
||||
fail when something in the test function panics. Each test is run in a new
|
||||
thread, and when the main thread sees that a test thread has died, the test is
|
||||
marked as failed. In Chapter 9, we talked about how the simplest way to panic
|
||||
is to call the <code>panic!</code> macro. Enter the new test as a function named
|
||||
<code>another</code>, so your <em>src/lib.rs</em> file looks like Listing 11-3.</p>
|
||||
<figure class="listing" id="listing-11-3">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust panics noplayground">pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn exploration() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn another() {
|
||||
panic!("Make this test fail");
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-3">Listing 11-3</a>: Adding a second test that will fail because we call the <code>panic!</code> macro</figcaption>
|
||||
</figure>
|
||||
<p>Run the tests again using <code>cargo test</code>. The output should look like Listing
|
||||
11-4, which shows that our <code>exploration</code> test passed and <code>another</code> failed.</p>
|
||||
<figure class="listing" id="listing-11-4">
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.72s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||||
|
||||
running 2 tests
|
||||
test tests::another ... FAILED
|
||||
test tests::exploration ... ok
|
||||
|
||||
failures:
|
||||
|
||||
---- tests::another stdout ----
|
||||
|
||||
thread 'tests::another' panicked at src/lib.rs:17:9:
|
||||
Make this test fail
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
|
||||
failures:
|
||||
tests::another
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
error: test failed, to rerun pass `--lib`
|
||||
</code></pre>
|
||||
<figcaption><a href="#listing-11-4">Listing 11-4</a>: Test results when one test passes and one test fails</figcaption>
|
||||
</figure>
|
||||
<!-- manual-regeneration
|
||||
rg panicked listings/ch11-writing-automated-tests/listing-11-03/output.txt
|
||||
check the line number of the panic matches the line number in the following paragraph
|
||||
-->
|
||||
<p>Instead of <code>ok</code>, the line <code>test tests::another</code> shows <code>FAILED</code>. Two new
|
||||
sections appear between the individual results and the summary: The first
|
||||
displays the detailed reason for each test failure. In this case, we get the
|
||||
details that <code>tests::another</code> failed because it panicked with the message <code>Make this test fail</code> on line 17 in the <em>src/lib.rs</em> file. The next section lists
|
||||
just the names of all the failing tests, which is useful when there are lots of
|
||||
tests and lots of detailed failing test output. We can use the name of a
|
||||
failing test to run just that test to debug it more easily; we’ll talk more
|
||||
about ways to run tests in the <a href="ch11-02-running-tests.html#controlling-how-tests-are-run">“Controlling How Tests Are
|
||||
Run”</a><!-- ignore --> section.</p>
|
||||
<p>The summary line displays at the end: Overall, our test result is <code>FAILED</code>. We
|
||||
had one test pass and one test fail.</p>
|
||||
<p>Now that you’ve seen what the test results look like in different scenarios,
|
||||
let’s look at some macros other than <code>panic!</code> that are useful in tests.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="checking-results-with-the-assert-macro"></a></p>
|
||||
<h3 id="checking-results-with-assert"><a class="header" href="#checking-results-with-assert">Checking Results with <code>assert!</code></a></h3>
|
||||
<p>The <code>assert!</code> macro, provided by the standard library, is useful when you want
|
||||
to ensure that some condition in a test evaluates to <code>true</code>. We give the
|
||||
<code>assert!</code> macro an argument that evaluates to a Boolean. If the value is
|
||||
<code>true</code>, nothing happens and the test passes. If the value is <code>false</code>, the
|
||||
<code>assert!</code> macro calls <code>panic!</code> to cause the test to fail. Using the <code>assert!</code>
|
||||
macro helps us check that our code is functioning in the way we intend.</p>
|
||||
<p>In Chapter 5, Listing 5-15, we used a <code>Rectangle</code> struct and a <code>can_hold</code>
|
||||
method, which are repeated here in Listing 11-5. Let’s put this code in the
|
||||
<em>src/lib.rs</em> file, then write some tests for it using the <code>assert!</code> macro.</p>
|
||||
<figure class="listing" id="listing-11-5">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust noplayground">#[derive(Debug)]
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
self.width > other.width && self.height > other.height
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-5">Listing 11-5</a>: The <code>Rectangle</code> struct and its <code>can_hold</code> method from Chapter 5</figcaption>
|
||||
</figure>
|
||||
<p>The <code>can_hold</code> method returns a Boolean, which means it’s a perfect use case
|
||||
for the <code>assert!</code> macro. In Listing 11-6, we write a test that exercises the
|
||||
<code>can_hold</code> method by creating a <code>Rectangle</code> instance that has a width of 8 and
|
||||
a height of 7 and asserting that it can hold another <code>Rectangle</code> instance that
|
||||
has a width of 5 and a height of 1.</p>
|
||||
<figure class="listing" id="listing-11-6">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust noplayground"><span class="boring">#[derive(Debug)]
|
||||
</span><span class="boring">struct Rectangle {
|
||||
</span><span class="boring"> width: u32,
|
||||
</span><span class="boring"> height: u32,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">impl Rectangle {
|
||||
</span><span class="boring"> fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
</span><span class="boring"> self.width > other.width && self.height > other.height
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span>#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn larger_can_hold_smaller() {
|
||||
let larger = Rectangle {
|
||||
width: 8,
|
||||
height: 7,
|
||||
};
|
||||
let smaller = Rectangle {
|
||||
width: 5,
|
||||
height: 1,
|
||||
};
|
||||
|
||||
assert!(larger.can_hold(&smaller));
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-6">Listing 11-6</a>: A test for <code>can_hold</code> that checks whether a larger rectangle can indeed hold a smaller rectangle</figcaption>
|
||||
</figure>
|
||||
<p>Note the <code>use super::*;</code> line inside the <code>tests</code> module. The <code>tests</code> module is
|
||||
a regular module that follows the usual visibility rules we covered in Chapter
|
||||
7 in the <a href="../ch07/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html">“Paths for Referring to an Item in the Module
|
||||
Tree”</a><!-- ignore -->
|
||||
section. Because the <code>tests</code> module is an inner module, we need to bring the
|
||||
code under test in the outer module into the scope of the inner module. We use
|
||||
a glob here, so anything we define in the outer module is available to this
|
||||
<code>tests</code> module.</p>
|
||||
<p>We’ve named our test <code>larger_can_hold_smaller</code>, and we’ve created the two
|
||||
<code>Rectangle</code> instances that we need. Then, we called the <code>assert!</code> macro and
|
||||
passed it the result of calling <code>larger.can_hold(&smaller)</code>. This expression is
|
||||
supposed to return <code>true</code>, so our test should pass. Let’s find out!</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling rectangle v0.1.0 (file:///projects/rectangle)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
|
||||
Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
|
||||
|
||||
running 1 test
|
||||
test tests::larger_can_hold_smaller ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests rectangle
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>It does pass! Let’s add another test, this time asserting that a smaller
|
||||
rectangle cannot hold a larger rectangle:</p>
|
||||
<p><span class="filename">Filename: src/lib.rs</span></p>
|
||||
<pre><code class="language-rust noplayground"><span class="boring">#[derive(Debug)]
|
||||
</span><span class="boring">struct Rectangle {
|
||||
</span><span class="boring"> width: u32,
|
||||
</span><span class="boring"> height: u32,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">impl Rectangle {
|
||||
</span><span class="boring"> fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
</span><span class="boring"> self.width > other.width && self.height > other.height
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span>#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn larger_can_hold_smaller() {
|
||||
// --snip--
|
||||
<span class="boring"> let larger = Rectangle {
|
||||
</span><span class="boring"> width: 8,
|
||||
</span><span class="boring"> height: 7,
|
||||
</span><span class="boring"> };
|
||||
</span><span class="boring"> let smaller = Rectangle {
|
||||
</span><span class="boring"> width: 5,
|
||||
</span><span class="boring"> height: 1,
|
||||
</span><span class="boring"> };
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> assert!(larger.can_hold(&smaller));
|
||||
</span> }
|
||||
|
||||
#[test]
|
||||
fn smaller_cannot_hold_larger() {
|
||||
let larger = Rectangle {
|
||||
width: 8,
|
||||
height: 7,
|
||||
};
|
||||
let smaller = Rectangle {
|
||||
width: 5,
|
||||
height: 1,
|
||||
};
|
||||
|
||||
assert!(!smaller.can_hold(&larger));
|
||||
}
|
||||
}</code></pre>
|
||||
<p>Because the correct result of the <code>can_hold</code> function in this case is <code>false</code>,
|
||||
we need to negate that result before we pass it to the <code>assert!</code> macro. As a
|
||||
result, our test will pass if <code>can_hold</code> returns <code>false</code>:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling rectangle v0.1.0 (file:///projects/rectangle)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
|
||||
Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
|
||||
|
||||
running 2 tests
|
||||
test tests::larger_can_hold_smaller ... ok
|
||||
test tests::smaller_cannot_hold_larger ... ok
|
||||
|
||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests rectangle
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>Two tests that pass! Now let’s see what happens to our test results when we
|
||||
introduce a bug in our code. We’ll change the implementation of the <code>can_hold</code>
|
||||
method by replacing the greater-than sign (<code>></code>) with a less-than sign (<code><</code>)
|
||||
when it compares the widths:</p>
|
||||
<pre><code class="language-rust not_desired_behavior noplayground"><span class="boring">#[derive(Debug)]
|
||||
</span><span class="boring">struct Rectangle {
|
||||
</span><span class="boring"> width: u32,
|
||||
</span><span class="boring"> height: u32,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span>// --snip--
|
||||
impl Rectangle {
|
||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
self.width < other.width && self.height > other.height
|
||||
}
|
||||
}
|
||||
<span class="boring">
|
||||
</span><span class="boring">#[cfg(test)]
|
||||
</span><span class="boring">mod tests {
|
||||
</span><span class="boring"> use super::*;
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> #[test]
|
||||
</span><span class="boring"> fn larger_can_hold_smaller() {
|
||||
</span><span class="boring"> let larger = Rectangle {
|
||||
</span><span class="boring"> width: 8,
|
||||
</span><span class="boring"> height: 7,
|
||||
</span><span class="boring"> };
|
||||
</span><span class="boring"> let smaller = Rectangle {
|
||||
</span><span class="boring"> width: 5,
|
||||
</span><span class="boring"> height: 1,
|
||||
</span><span class="boring"> };
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> assert!(larger.can_hold(&smaller));
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> #[test]
|
||||
</span><span class="boring"> fn smaller_cannot_hold_larger() {
|
||||
</span><span class="boring"> let larger = Rectangle {
|
||||
</span><span class="boring"> width: 8,
|
||||
</span><span class="boring"> height: 7,
|
||||
</span><span class="boring"> };
|
||||
</span><span class="boring"> let smaller = Rectangle {
|
||||
</span><span class="boring"> width: 5,
|
||||
</span><span class="boring"> height: 1,
|
||||
</span><span class="boring"> };
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> assert!(!smaller.can_hold(&larger));
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}</span></code></pre>
|
||||
<p>Running the tests now produces the following:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling rectangle v0.1.0 (file:///projects/rectangle)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
|
||||
Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
|
||||
|
||||
running 2 tests
|
||||
test tests::larger_can_hold_smaller ... FAILED
|
||||
test tests::smaller_cannot_hold_larger ... ok
|
||||
|
||||
failures:
|
||||
|
||||
---- tests::larger_can_hold_smaller stdout ----
|
||||
|
||||
thread 'tests::larger_can_hold_smaller' panicked at src/lib.rs:28:9:
|
||||
assertion failed: larger.can_hold(&smaller)
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
|
||||
failures:
|
||||
tests::larger_can_hold_smaller
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
error: test failed, to rerun pass `--lib`
|
||||
</code></pre>
|
||||
<p>Our tests caught the bug! Because <code>larger.width</code> is <code>8</code> and <code>smaller.width</code> is
|
||||
<code>5</code>, the comparison of the widths in <code>can_hold</code> now returns <code>false</code>: 8 is not
|
||||
less than 5.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="testing-equality-with-the-assert_eq-and-assert_ne-macros"></a></p>
|
||||
<h3 id="testing-equality-with-assert_eq-and-assert_ne"><a class="header" href="#testing-equality-with-assert_eq-and-assert_ne">Testing Equality with <code>assert_eq!</code> and <code>assert_ne!</code></a></h3>
|
||||
<p>A common way to verify functionality is to test for equality between the result
|
||||
of the code under test and the value you expect the code to return. You could
|
||||
do this by using the <code>assert!</code> macro and passing it an expression using the
|
||||
<code>==</code> operator. However, this is such a common test that the standard library
|
||||
provides a pair of macros—<code>assert_eq!</code> and <code>assert_ne!</code>—to perform this test
|
||||
more conveniently. These macros compare two arguments for equality or
|
||||
inequality, respectively. They’ll also print the two values if the assertion
|
||||
fails, which makes it easier to see <em>why</em> the test failed; conversely, the
|
||||
<code>assert!</code> macro only indicates that it got a <code>false</code> value for the <code>==</code>
|
||||
expression, without printing the values that led to the <code>false</code> value.</p>
|
||||
<p>In Listing 11-7, we write a function named <code>add_two</code> that adds <code>2</code> to its
|
||||
parameter, and then we test this function using the <code>assert_eq!</code> macro.</p>
|
||||
<figure class="listing" id="listing-11-7">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust noplayground">pub fn add_two(a: u64) -> u64 {
|
||||
a + 2
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_adds_two() {
|
||||
let result = add_two(2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-7">Listing 11-7</a>: Testing the function <code>add_two</code> using the <code>assert_eq!</code> macro</figcaption>
|
||||
</figure>
|
||||
<p>Let’s check that it passes!</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.58s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||||
|
||||
running 1 test
|
||||
test tests::it_adds_two ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests adder
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>We create a variable named <code>result</code> that holds the result of calling
|
||||
<code>add_two(2)</code>. Then, we pass <code>result</code> and <code>4</code> as the arguments to the
|
||||
<code>assert_eq!</code> macro. The output line for this test is <code>test tests::it_adds_two ... ok</code>, and the <code>ok</code> text indicates that our test passed!</p>
|
||||
<p>Let’s introduce a bug into our code to see what <code>assert_eq!</code> looks like when it
|
||||
fails. Change the implementation of the <code>add_two</code> function to instead add <code>3</code>:</p>
|
||||
<pre><code class="language-rust not_desired_behavior noplayground">pub fn add_two(a: u64) -> u64 {
|
||||
a + 3
|
||||
}
|
||||
<span class="boring">
|
||||
</span><span class="boring">#[cfg(test)]
|
||||
</span><span class="boring">mod tests {
|
||||
</span><span class="boring"> use super::*;
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> #[test]
|
||||
</span><span class="boring"> fn it_adds_two() {
|
||||
</span><span class="boring"> let result = add_two(2);
|
||||
</span><span class="boring"> assert_eq!(result, 4);
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}</span></code></pre>
|
||||
<p>Run the tests again:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||||
|
||||
running 1 test
|
||||
test tests::it_adds_two ... FAILED
|
||||
|
||||
failures:
|
||||
|
||||
---- tests::it_adds_two stdout ----
|
||||
|
||||
thread 'tests::it_adds_two' panicked at src/lib.rs:12:9:
|
||||
assertion `left == right` failed
|
||||
left: 5
|
||||
right: 4
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
|
||||
failures:
|
||||
tests::it_adds_two
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
error: test failed, to rerun pass `--lib`
|
||||
</code></pre>
|
||||
<p>Our test caught the bug! The <code>tests::it_adds_two</code> test failed, and the message
|
||||
tells us that the assertion that failed was <code>left == right</code> and what the <code>left</code>
|
||||
and <code>right</code> values are. This message helps us start debugging: The <code>left</code>
|
||||
argument, where we had the result of calling <code>add_two(2)</code>, was <code>5</code>, but the
|
||||
<code>right</code> argument was <code>4</code>. You can imagine that this would be especially helpful
|
||||
when we have a lot of tests going on.</p>
|
||||
<p>Note that in some languages and test frameworks, the parameters to equality
|
||||
assertion functions are called <code>expected</code> and <code>actual</code>, and the order in which
|
||||
we specify the arguments matters. However, in Rust, they’re called <code>left</code> and
|
||||
<code>right</code>, and the order in which we specify the value we expect and the value
|
||||
the code produces doesn’t matter. We could write the assertion in this test as
|
||||
<code>assert_eq!(4, result)</code>, which would result in the same failure message that
|
||||
displays <code>assertion `left == right` failed</code>.</p>
|
||||
<p>The <code>assert_ne!</code> macro will pass if the two values we give it are not equal and
|
||||
will fail if they are equal. This macro is most useful for cases when we’re not
|
||||
sure what a value <em>will</em> be, but we know what the value definitely <em>shouldn’t</em>
|
||||
be. For example, if we’re testing a function that is guaranteed to change its
|
||||
input in some way, but the way in which the input is changed depends on the day
|
||||
of the week that we run our tests, the best thing to assert might be that the
|
||||
output of the function is not equal to the input.</p>
|
||||
<p>Under the surface, the <code>assert_eq!</code> and <code>assert_ne!</code> macros use the operators
|
||||
<code>==</code> and <code>!=</code>, respectively. When the assertions fail, these macros print their
|
||||
arguments using debug formatting, which means the values being compared must
|
||||
implement the <code>PartialEq</code> and <code>Debug</code> traits. All primitive types and most of
|
||||
the standard library types implement these traits. For structs and enums that
|
||||
you define yourself, you’ll need to implement <code>PartialEq</code> to assert equality of
|
||||
those types. You’ll also need to implement <code>Debug</code> to print the values when the
|
||||
assertion fails. Because both traits are derivable traits, as mentioned in
|
||||
Listing 5-12 in Chapter 5, this is usually as straightforward as adding the
|
||||
<code>#[derive(PartialEq, Debug)]</code> annotation to your struct or enum definition. See
|
||||
Appendix C, <a href="../appendix/appendix-03-derivable-traits.html">“Derivable Traits,”</a><!-- ignore --> for more
|
||||
details about these and other derivable traits.</p>
|
||||
<h3 id="adding-custom-failure-messages"><a class="header" href="#adding-custom-failure-messages">Adding Custom Failure Messages</a></h3>
|
||||
<p>You can also add a custom message to be printed with the failure message as
|
||||
optional arguments to the <code>assert!</code>, <code>assert_eq!</code>, and <code>assert_ne!</code> macros. Any
|
||||
arguments specified after the required arguments are passed along to the
|
||||
<code>format!</code> macro (discussed in <a href="../ch08/ch08-02-strings.html#concatenating-with--or-format">“Concatenating with <code>+</code> or
|
||||
<code>format!</code>”</a><!--
|
||||
ignore --> in Chapter 8), so you can pass a format string that contains <code>{}</code>
|
||||
placeholders and values to go in those placeholders. Custom messages are useful
|
||||
for documenting what an assertion means; when a test fails, you’ll have a better
|
||||
idea of what the problem is with the code.</p>
|
||||
<p>For example, let’s say we have a function that greets people by name and we
|
||||
want to test that the name we pass into the function appears in the output:</p>
|
||||
<p><span class="filename">Filename: src/lib.rs</span></p>
|
||||
<pre><code class="language-rust noplayground">pub fn greeting(name: &str) -> String {
|
||||
format!("Hello {name}!")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn greeting_contains_name() {
|
||||
let result = greeting("Carol");
|
||||
assert!(result.contains("Carol"));
|
||||
}
|
||||
}</code></pre>
|
||||
<p>The requirements for this program haven’t been agreed upon yet, and we’re
|
||||
pretty sure the <code>Hello</code> text at the beginning of the greeting will change. We
|
||||
decided we don’t want to have to update the test when the requirements change,
|
||||
so instead of checking for exact equality to the value returned from the
|
||||
<code>greeting</code> function, we’ll just assert that the output contains the text of the
|
||||
input parameter.</p>
|
||||
<p>Now let’s introduce a bug into this code by changing <code>greeting</code> to exclude
|
||||
<code>name</code> to see what the default test failure looks like:</p>
|
||||
<pre><code class="language-rust not_desired_behavior noplayground">pub fn greeting(name: &str) -> String {
|
||||
String::from("Hello!")
|
||||
}
|
||||
<span class="boring">
|
||||
</span><span class="boring">#[cfg(test)]
|
||||
</span><span class="boring">mod tests {
|
||||
</span><span class="boring"> use super::*;
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> #[test]
|
||||
</span><span class="boring"> fn greeting_contains_name() {
|
||||
</span><span class="boring"> let result = greeting("Carol");
|
||||
</span><span class="boring"> assert!(result.contains("Carol"));
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}</span></code></pre>
|
||||
<p>Running this test produces the following:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling greeter v0.1.0 (file:///projects/greeter)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.91s
|
||||
Running unittests src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)
|
||||
|
||||
running 1 test
|
||||
test tests::greeting_contains_name ... FAILED
|
||||
|
||||
failures:
|
||||
|
||||
---- tests::greeting_contains_name stdout ----
|
||||
|
||||
thread 'tests::greeting_contains_name' panicked at src/lib.rs:12:9:
|
||||
assertion failed: result.contains("Carol")
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
|
||||
failures:
|
||||
tests::greeting_contains_name
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
error: test failed, to rerun pass `--lib`
|
||||
</code></pre>
|
||||
<p>This result just indicates that the assertion failed and which line the
|
||||
assertion is on. A more useful failure message would print the value from the
|
||||
<code>greeting</code> function. Let’s add a custom failure message composed of a format
|
||||
string with a placeholder filled in with the actual value we got from the
|
||||
<code>greeting</code> function:</p>
|
||||
<pre><code class="language-rust ignore"><span class="boring">pub fn greeting(name: &str) -> String {
|
||||
</span><span class="boring"> String::from("Hello!")
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">#[cfg(test)]
|
||||
</span><span class="boring">mod tests {
|
||||
</span><span class="boring"> use super::*;
|
||||
</span><span class="boring">
|
||||
</span> #[test]
|
||||
fn greeting_contains_name() {
|
||||
let result = greeting("Carol");
|
||||
assert!(
|
||||
result.contains("Carol"),
|
||||
"Greeting did not contain name, value was `{result}`"
|
||||
);
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<p>Now when we run the test, we’ll get a more informative error message:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling greeter v0.1.0 (file:///projects/greeter)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.93s
|
||||
Running unittests src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)
|
||||
|
||||
running 1 test
|
||||
test tests::greeting_contains_name ... FAILED
|
||||
|
||||
failures:
|
||||
|
||||
---- tests::greeting_contains_name stdout ----
|
||||
|
||||
thread 'tests::greeting_contains_name' panicked at src/lib.rs:12:9:
|
||||
Greeting did not contain name, value was `Hello!`
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
|
||||
failures:
|
||||
tests::greeting_contains_name
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
error: test failed, to rerun pass `--lib`
|
||||
</code></pre>
|
||||
<p>We can see the value we actually got in the test output, which would help us
|
||||
debug what happened instead of what we were expecting to happen.</p>
|
||||
<h3 id="checking-for-panics-with-should_panic"><a class="header" href="#checking-for-panics-with-should_panic">Checking for Panics with <code>should_panic</code></a></h3>
|
||||
<p>In addition to checking return values, it’s important to check that our code
|
||||
handles error conditions as we expect. For example, consider the <code>Guess</code> type
|
||||
that we created in Chapter 9, Listing 9-13. Other code that uses <code>Guess</code>
|
||||
depends on the guarantee that <code>Guess</code> instances will contain only values
|
||||
between 1 and 100. We can write a test that ensures that attempting to create a
|
||||
<code>Guess</code> instance with a value outside that range panics.</p>
|
||||
<p>We do this by adding the attribute <code>should_panic</code> to our test function. The
|
||||
test passes if the code inside the function panics; the test fails if the code
|
||||
inside the function doesn’t panic.</p>
|
||||
<p>Listing 11-8 shows a test that checks that the error conditions of <code>Guess::new</code>
|
||||
happen when we expect them to.</p>
|
||||
<figure class="listing" id="listing-11-8">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust noplayground">pub struct Guess {
|
||||
value: i32,
|
||||
}
|
||||
|
||||
impl Guess {
|
||||
pub fn new(value: i32) -> Guess {
|
||||
if value < 1 || value > 100 {
|
||||
panic!("Guess value must be between 1 and 100, got {value}.");
|
||||
}
|
||||
|
||||
Guess { value }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn greater_than_100() {
|
||||
Guess::new(200);
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-8">Listing 11-8</a>: Testing that a condition will cause a <code>panic!</code></figcaption>
|
||||
</figure>
|
||||
<p>We place the <code>#[should_panic]</code> attribute after the <code>#[test]</code> attribute and
|
||||
before the test function it applies to. Let’s look at the result when this test
|
||||
passes:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.58s
|
||||
Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
|
||||
|
||||
running 1 test
|
||||
test tests::greater_than_100 - should panic ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests guessing_game
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>Looks good! Now let’s introduce a bug in our code by removing the condition
|
||||
that the <code>new</code> function will panic if the value is greater than 100:</p>
|
||||
<pre><code class="language-rust not_desired_behavior noplayground"><span class="boring">pub struct Guess {
|
||||
</span><span class="boring"> value: i32,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span>// --snip--
|
||||
impl Guess {
|
||||
pub fn new(value: i32) -> Guess {
|
||||
if value < 1 {
|
||||
panic!("Guess value must be between 1 and 100, got {value}.");
|
||||
}
|
||||
|
||||
Guess { value }
|
||||
}
|
||||
}
|
||||
<span class="boring">
|
||||
</span><span class="boring">#[cfg(test)]
|
||||
</span><span class="boring">mod tests {
|
||||
</span><span class="boring"> use super::*;
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> #[test]
|
||||
</span><span class="boring"> #[should_panic]
|
||||
</span><span class="boring"> fn greater_than_100() {
|
||||
</span><span class="boring"> Guess::new(200);
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}</span></code></pre>
|
||||
<p>When we run the test in Listing 11-8, it will fail:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.62s
|
||||
Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
|
||||
|
||||
running 1 test
|
||||
test tests::greater_than_100 - should panic ... FAILED
|
||||
|
||||
failures:
|
||||
|
||||
---- tests::greater_than_100 stdout ----
|
||||
note: test did not panic as expected at src/lib.rs:21:8
|
||||
|
||||
failures:
|
||||
tests::greater_than_100
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
error: test failed, to rerun pass `--lib`
|
||||
</code></pre>
|
||||
<p>We don’t get a very helpful message in this case, but when we look at the test
|
||||
function, we see that it’s annotated with <code>#[should_panic]</code>. The failure we got
|
||||
means that the code in the test function did not cause a panic.</p>
|
||||
<p>Tests that use <code>should_panic</code> can be imprecise. A <code>should_panic</code> test would
|
||||
pass even if the test panics for a different reason from the one we were
|
||||
expecting. To make <code>should_panic</code> tests more precise, we can add an optional
|
||||
<code>expected</code> parameter to the <code>should_panic</code> attribute. The test harness will
|
||||
make sure that the failure message contains the provided text. For example,
|
||||
consider the modified code for <code>Guess</code> in Listing 11-9 where the <code>new</code> function
|
||||
panics with different messages depending on whether the value is too small or
|
||||
too large.</p>
|
||||
<figure class="listing" id="listing-11-9">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust noplayground"><span class="boring">pub struct Guess {
|
||||
</span><span class="boring"> value: i32,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span>// --snip--
|
||||
|
||||
impl Guess {
|
||||
pub fn new(value: i32) -> Guess {
|
||||
if value < 1 {
|
||||
panic!(
|
||||
"Guess value must be greater than or equal to 1, got {value}."
|
||||
);
|
||||
} else if value > 100 {
|
||||
panic!(
|
||||
"Guess value must be less than or equal to 100, got {value}."
|
||||
);
|
||||
}
|
||||
|
||||
Guess { value }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "less than or equal to 100")]
|
||||
fn greater_than_100() {
|
||||
Guess::new(200);
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-9">Listing 11-9</a>: Testing for a <code>panic!</code> with a panic message containing a specified substring</figcaption>
|
||||
</figure>
|
||||
<p>This test will pass because the value we put in the <code>should_panic</code> attribute’s
|
||||
<code>expected</code> parameter is a substring of the message that the <code>Guess::new</code>
|
||||
function panics with. We could have specified the entire panic message that we
|
||||
expect, which in this case would be <code>Guess value must be less than or equal to 100, got 200</code>. What you choose to specify depends on how much of the panic
|
||||
message is unique or dynamic and how precise you want your test to be. In this
|
||||
case, a substring of the panic message is enough to ensure that the code in the
|
||||
test function executes the <code>else if value > 100</code> case.</p>
|
||||
<p>To see what happens when a <code>should_panic</code> test with an <code>expected</code> message
|
||||
fails, let’s again introduce a bug into our code by swapping the bodies of the
|
||||
<code>if value < 1</code> and the <code>else if value > 100</code> blocks:</p>
|
||||
<pre><code class="language-rust ignore not_desired_behavior"><span class="boring">pub struct Guess {
|
||||
</span><span class="boring"> value: i32,
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">impl Guess {
|
||||
</span><span class="boring"> pub fn new(value: i32) -> Guess {
|
||||
</span> if value < 1 {
|
||||
panic!(
|
||||
"Guess value must be less than or equal to 100, got {value}."
|
||||
);
|
||||
} else if value > 100 {
|
||||
panic!(
|
||||
"Guess value must be greater than or equal to 1, got {value}."
|
||||
);
|
||||
}
|
||||
<span class="boring">
|
||||
</span><span class="boring"> Guess { value }
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span><span class="boring">#[cfg(test)]
|
||||
</span><span class="boring">mod tests {
|
||||
</span><span class="boring"> use super::*;
|
||||
</span><span class="boring">
|
||||
</span><span class="boring"> #[test]
|
||||
</span><span class="boring"> #[should_panic(expected = "less than or equal to 100")]
|
||||
</span><span class="boring"> fn greater_than_100() {
|
||||
</span><span class="boring"> Guess::new(200);
|
||||
</span><span class="boring"> }
|
||||
</span><span class="boring">}</span></code></pre>
|
||||
<p>This time when we run the <code>should_panic</code> test, it will fail:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
|
||||
Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
|
||||
|
||||
running 1 test
|
||||
test tests::greater_than_100 - should panic ... FAILED
|
||||
|
||||
failures:
|
||||
|
||||
---- tests::greater_than_100 stdout ----
|
||||
|
||||
thread 'tests::greater_than_100' panicked at src/lib.rs:12:13:
|
||||
Guess value must be greater than or equal to 1, got 200.
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
note: panic did not contain expected string
|
||||
panic message: "Guess value must be greater than or equal to 1, got 200."
|
||||
expected substring: "less than or equal to 100"
|
||||
|
||||
failures:
|
||||
tests::greater_than_100
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
error: test failed, to rerun pass `--lib`
|
||||
</code></pre>
|
||||
<p>The failure message indicates that this test did indeed panic as we expected,
|
||||
but the panic message did not include the expected string <code>less than or equal to 100</code>. The panic message that we did get in this case was <code>Guess value must be greater than or equal to 1, got 200</code>. Now we can start figuring out where
|
||||
our bug is!</p>
|
||||
<h3 id="using-resultt-e-in-tests"><a class="header" href="#using-resultt-e-in-tests">Using <code>Result<T, E></code> in Tests</a></h3>
|
||||
<p>All of our tests so far panic when they fail. We can also write tests that use
|
||||
<code>Result<T, E></code>! Here’s the test from Listing 11-1, rewritten to use <code>Result<T, E></code> and return an <code>Err</code> instead of panicking:</p>
|
||||
<pre><code class="language-rust noplayground"><span class="boring">pub fn add(left: u64, right: u64) -> u64 {
|
||||
</span><span class="boring"> left + right
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span>#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() -> Result<(), String> {
|
||||
let result = add(2, 2);
|
||||
|
||||
if result == 4 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(String::from("two plus two does not equal four"))
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
<p>The <code>it_works</code> function now has the <code>Result<(), String></code> return type. In the
|
||||
body of the function, rather than calling the <code>assert_eq!</code> macro, we return
|
||||
<code>Ok(())</code> when the test passes and an <code>Err</code> with a <code>String</code> inside when the test
|
||||
fails.</p>
|
||||
<p>Writing tests so that they return a <code>Result<T, E></code> enables you to use the
|
||||
question mark operator in the body of tests, which can be a convenient way to
|
||||
write tests that should fail if any operation within them returns an <code>Err</code>
|
||||
variant.</p>
|
||||
<p>You can’t use the <code>#[should_panic]</code> annotation on tests that use <code>Result<T, E></code>. To assert that an operation returns an <code>Err</code> variant, <em>don’t</em> use the
|
||||
question mark operator on the <code>Result<T, E></code> value. Instead, use
|
||||
<code>assert!(value.is_err())</code>.</p>
|
||||
<p>Now that you know several ways to write tests, let’s look at what is happening
|
||||
when we run our tests and explore the different options we can use with <code>cargo test</code>.</p>
|
||||
</body>
|
||||
</html>
|
||||
327
ch11/ch11-02-running-tests.html
Normal file
327
ch11/ch11-02-running-tests.html
Normal file
@@ -0,0 +1,327 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Controlling How Tests Are Run</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id="controlling-how-tests-are-run"><a class="header" href="#controlling-how-tests-are-run">Controlling How Tests Are Run</a></h2>
|
||||
<p>Just as <code>cargo run</code> compiles your code and then runs the resultant binary,
|
||||
<code>cargo test</code> compiles your code in test mode and runs the resultant test
|
||||
binary. The default behavior of the binary produced by <code>cargo test</code> is to run
|
||||
all the tests in parallel and capture output generated during test runs,
|
||||
preventing the output from being displayed and making it easier to read the
|
||||
output related to the test results. You can, however, specify command line
|
||||
options to change this default behavior.</p>
|
||||
<p>Some command line options go to <code>cargo test</code>, and some go to the resultant test
|
||||
binary. To separate these two types of arguments, you list the arguments that
|
||||
go to <code>cargo test</code> followed by the separator <code>--</code> and then the ones that go to
|
||||
the test binary. Running <code>cargo test --help</code> displays the options you can use
|
||||
with <code>cargo test</code>, and running <code>cargo test -- --help</code> displays the options you
|
||||
can use after the separator. These options are also documented in <a href="https://doc.rust-lang.org/rustc/tests/index.html">the “Tests”
|
||||
section of <em>The <code>rustc</code> Book</em></a>.</p>
|
||||
<h3 id="running-tests-in-parallel-or-consecutively"><a class="header" href="#running-tests-in-parallel-or-consecutively">Running Tests in Parallel or Consecutively</a></h3>
|
||||
<p>When you run multiple tests, by default they run in parallel using threads,
|
||||
meaning they finish running more quickly and you get feedback sooner. Because
|
||||
the tests are running at the same time, you must make sure your tests don’t
|
||||
depend on each other or on any shared state, including a shared environment,
|
||||
such as the current working directory or environment variables.</p>
|
||||
<p>For example, say each of your tests runs some code that creates a file on disk
|
||||
named <em>test-output.txt</em> and writes some data to that file. Then, each test
|
||||
reads the data in that file and asserts that the file contains a particular
|
||||
value, which is different in each test. Because the tests run at the same time,
|
||||
one test might overwrite the file in the time between when another test is
|
||||
writing and reading the file. The second test will then fail, not because the
|
||||
code is incorrect but because the tests have interfered with each other while
|
||||
running in parallel. One solution is to make sure each test writes to a
|
||||
different file; another solution is to run the tests one at a time.</p>
|
||||
<p>If you don’t want to run the tests in parallel or if you want more fine-grained
|
||||
control over the number of threads used, you can send the <code>--test-threads</code> flag
|
||||
and the number of threads you want to use to the test binary. Take a look at
|
||||
the following example:</p>
|
||||
<pre><code class="language-console">$ cargo test -- --test-threads=1
|
||||
</code></pre>
|
||||
<p>We set the number of test threads to <code>1</code>, telling the program not to use any
|
||||
parallelism. Running the tests using one thread will take longer than running
|
||||
them in parallel, but the tests won’t interfere with each other if they share
|
||||
state.</p>
|
||||
<h3 id="showing-function-output"><a class="header" href="#showing-function-output">Showing Function Output</a></h3>
|
||||
<p>By default, if a test passes, Rust’s test library captures anything printed to
|
||||
standard output. For example, if we call <code>println!</code> in a test and the test
|
||||
passes, we won’t see the <code>println!</code> output in the terminal; we’ll see only the
|
||||
line that indicates the test passed. If a test fails, we’ll see whatever was
|
||||
printed to standard output with the rest of the failure message.</p>
|
||||
<p>As an example, Listing 11-10 has a silly function that prints the value of its
|
||||
parameter and returns 10, as well as a test that passes and a test that fails.</p>
|
||||
<figure class="listing" id="listing-11-10">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust panics noplayground">fn prints_and_returns_10(a: i32) -> i32 {
|
||||
println!("I got the value {a}");
|
||||
10
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn this_test_will_pass() {
|
||||
let value = prints_and_returns_10(4);
|
||||
assert_eq!(value, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn this_test_will_fail() {
|
||||
let value = prints_and_returns_10(8);
|
||||
assert_eq!(value, 5);
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-10">Listing 11-10</a>: Tests for a function that calls <code>println!</code></figcaption>
|
||||
</figure>
|
||||
<p>When we run these tests with <code>cargo test</code>, we’ll see the following output:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling silly-function v0.1.0 (file:///projects/silly-function)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.58s
|
||||
Running unittests src/lib.rs (target/debug/deps/silly_function-160869f38cff9166)
|
||||
|
||||
running 2 tests
|
||||
test tests::this_test_will_fail ... FAILED
|
||||
test tests::this_test_will_pass ... ok
|
||||
|
||||
failures:
|
||||
|
||||
---- tests::this_test_will_fail stdout ----
|
||||
I got the value 8
|
||||
|
||||
thread 'tests::this_test_will_fail' panicked at src/lib.rs:19:9:
|
||||
assertion `left == right` failed
|
||||
left: 10
|
||||
right: 5
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
|
||||
failures:
|
||||
tests::this_test_will_fail
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
error: test failed, to rerun pass `--lib`
|
||||
</code></pre>
|
||||
<p>Note that nowhere in this output do we see <code>I got the value 4</code>, which is
|
||||
printed when the test that passes runs. That output has been captured. The
|
||||
output from the test that failed, <code>I got the value 8</code>, appears in the section
|
||||
of the test summary output, which also shows the cause of the test failure.</p>
|
||||
<p>If we want to see printed values for passing tests as well, we can tell Rust to
|
||||
also show the output of successful tests with <code>--show-output</code>:</p>
|
||||
<pre><code class="language-console">$ cargo test -- --show-output
|
||||
</code></pre>
|
||||
<p>When we run the tests in Listing 11-10 again with the <code>--show-output</code> flag, we
|
||||
see the following output:</p>
|
||||
<pre><code class="language-console">$ cargo test -- --show-output
|
||||
Compiling silly-function v0.1.0 (file:///projects/silly-function)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.60s
|
||||
Running unittests src/lib.rs (target/debug/deps/silly_function-160869f38cff9166)
|
||||
|
||||
running 2 tests
|
||||
test tests::this_test_will_fail ... FAILED
|
||||
test tests::this_test_will_pass ... ok
|
||||
|
||||
successes:
|
||||
|
||||
---- tests::this_test_will_pass stdout ----
|
||||
I got the value 4
|
||||
|
||||
|
||||
successes:
|
||||
tests::this_test_will_pass
|
||||
|
||||
failures:
|
||||
|
||||
---- tests::this_test_will_fail stdout ----
|
||||
I got the value 8
|
||||
|
||||
thread 'tests::this_test_will_fail' panicked at src/lib.rs:19:9:
|
||||
assertion `left == right` failed
|
||||
left: 10
|
||||
right: 5
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
|
||||
failures:
|
||||
tests::this_test_will_fail
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
error: test failed, to rerun pass `--lib`
|
||||
</code></pre>
|
||||
<h3 id="running-a-subset-of-tests-by-name"><a class="header" href="#running-a-subset-of-tests-by-name">Running a Subset of Tests by Name</a></h3>
|
||||
<p>Running a full test suite can sometimes take a long time. If you’re working on
|
||||
code in a particular area, you might want to run only the tests pertaining to
|
||||
that code. You can choose which tests to run by passing <code>cargo test</code> the name
|
||||
or names of the test(s) you want to run as an argument.</p>
|
||||
<p>To demonstrate how to run a subset of tests, we’ll first create three tests for
|
||||
our <code>add_two</code> function, as shown in Listing 11-11, and choose which ones to run.</p>
|
||||
<figure class="listing" id="listing-11-11">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust noplayground">pub fn add_two(a: u64) -> u64 {
|
||||
a + 2
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn add_two_and_two() {
|
||||
let result = add_two(2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_three_and_two() {
|
||||
let result = add_two(3);
|
||||
assert_eq!(result, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_hundred() {
|
||||
let result = add_two(100);
|
||||
assert_eq!(result, 102);
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-11">Listing 11-11</a>: Three tests with three different names</figcaption>
|
||||
</figure>
|
||||
<p>If we run the tests without passing any arguments, as we saw earlier, all the
|
||||
tests will run in parallel:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.62s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||||
|
||||
running 3 tests
|
||||
test tests::add_three_and_two ... ok
|
||||
test tests::add_two_and_two ... ok
|
||||
test tests::one_hundred ... ok
|
||||
|
||||
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests adder
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<h4 id="running-single-tests"><a class="header" href="#running-single-tests">Running Single Tests</a></h4>
|
||||
<p>We can pass the name of any test function to <code>cargo test</code> to run only that test:</p>
|
||||
<pre><code class="language-console">$ cargo test one_hundred
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.69s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||||
|
||||
running 1 test
|
||||
test tests::one_hundred ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>Only the test with the name <code>one_hundred</code> ran; the other two tests didn’t match
|
||||
that name. The test output lets us know we had more tests that didn’t run by
|
||||
displaying <code>2 filtered out</code> at the end.</p>
|
||||
<p>We can’t specify the names of multiple tests in this way; only the first value
|
||||
given to <code>cargo test</code> will be used. But there is a way to run multiple tests.</p>
|
||||
<h4 id="filtering-to-run-multiple-tests"><a class="header" href="#filtering-to-run-multiple-tests">Filtering to Run Multiple Tests</a></h4>
|
||||
<p>We can specify part of a test name, and any test whose name matches that value
|
||||
will be run. For example, because two of our tests’ names contain <code>add</code>, we can
|
||||
run those two by running <code>cargo test add</code>:</p>
|
||||
<pre><code class="language-console">$ cargo test add
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||||
|
||||
running 2 tests
|
||||
test tests::add_three_and_two ... ok
|
||||
test tests::add_two_and_two ... ok
|
||||
|
||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>This command ran all tests with <code>add</code> in the name and filtered out the test
|
||||
named <code>one_hundred</code>. Also note that the module in which a test appears becomes
|
||||
part of the test’s name, so we can run all the tests in a module by filtering
|
||||
on the module’s name.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="ignoring-some-tests-unless-specifically-requested"></a></p>
|
||||
<h3 id="ignoring-tests-unless-specifically-requested"><a class="header" href="#ignoring-tests-unless-specifically-requested">Ignoring Tests Unless Specifically Requested</a></h3>
|
||||
<p>Sometimes a few specific tests can be very time-consuming to execute, so you
|
||||
might want to exclude them during most runs of <code>cargo test</code>. Rather than
|
||||
listing as arguments all tests you do want to run, you can instead annotate the
|
||||
time-consuming tests using the <code>ignore</code> attribute to exclude them, as shown
|
||||
here:</p>
|
||||
<p><span class="filename">Filename: src/lib.rs</span></p>
|
||||
<pre><code class="language-rust noplayground"><span class="boring">pub fn add(left: u64, right: u64) -> u64 {
|
||||
</span><span class="boring"> left + right
|
||||
</span><span class="boring">}
|
||||
</span><span class="boring">
|
||||
</span>#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn expensive_test() {
|
||||
// code that takes an hour to run
|
||||
}
|
||||
}</code></pre>
|
||||
<p>After <code>#[test]</code>, we add the <code>#[ignore]</code> line to the test we want to exclude.
|
||||
Now when we run our tests, <code>it_works</code> runs, but <code>expensive_test</code> doesn’t:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.60s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||||
|
||||
running 2 tests
|
||||
test tests::expensive_test ... ignored
|
||||
test tests::it_works ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests adder
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>The <code>expensive_test</code> function is listed as <code>ignored</code>. If we want to run only
|
||||
the ignored tests, we can use <code>cargo test -- --ignored</code>:</p>
|
||||
<pre><code class="language-console">$ cargo test -- --ignored
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||||
|
||||
running 1 test
|
||||
test tests::expensive_test ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests adder
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>By controlling which tests run, you can make sure your <code>cargo test</code> results
|
||||
will be returned quickly. When you’re at a point where it makes sense to check
|
||||
the results of the <code>ignored</code> tests and you have time to wait for the results,
|
||||
you can run <code>cargo test -- --ignored</code> instead. If you want to run all tests
|
||||
whether they’re ignored or not, you can run <code>cargo test -- --include-ignored</code>.</p>
|
||||
</body>
|
||||
</html>
|
||||
306
ch11/ch11-03-test-organization.html
Normal file
306
ch11/ch11-03-test-organization.html
Normal file
@@ -0,0 +1,306 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test Organization</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id="test-organization"><a class="header" href="#test-organization">Test Organization</a></h2>
|
||||
<p>As mentioned at the start of the chapter, testing is a complex discipline, and
|
||||
different people use different terminology and organization. The Rust community
|
||||
thinks about tests in terms of two main categories: unit tests and integration
|
||||
tests. <em>Unit tests</em> are small and more focused, testing one module in isolation
|
||||
at a time, and can test private interfaces. <em>Integration tests</em> are entirely
|
||||
external to your library and use your code in the same way any other external
|
||||
code would, using only the public interface and potentially exercising multiple
|
||||
modules per test.</p>
|
||||
<p>Writing both kinds of tests is important to ensure that the pieces of your
|
||||
library are doing what you expect them to, separately and together.</p>
|
||||
<h3 id="unit-tests"><a class="header" href="#unit-tests">Unit Tests</a></h3>
|
||||
<p>The purpose of unit tests is to test each unit of code in isolation from the
|
||||
rest of the code to quickly pinpoint where code is and isn’t working as
|
||||
expected. You’ll put unit tests in the <em>src</em> directory in each file with the
|
||||
code that they’re testing. The convention is to create a module named <code>tests</code>
|
||||
in each file to contain the test functions and to annotate the module with
|
||||
<code>cfg(test)</code>.</p>
|
||||
<h4 id="the-tests-module-and-cfgtest"><a class="header" href="#the-tests-module-and-cfgtest">The <code>tests</code> Module and <code>#[cfg(test)]</code></a></h4>
|
||||
<p>The <code>#[cfg(test)]</code> annotation on the <code>tests</code> module tells Rust to compile and
|
||||
run the test code only when you run <code>cargo test</code>, not when you run <code>cargo build</code>. This saves compile time when you only want to build the library and
|
||||
saves space in the resultant compiled artifact because the tests are not
|
||||
included. You’ll see that because integration tests go in a different
|
||||
directory, they don’t need the <code>#[cfg(test)]</code> annotation. However, because unit
|
||||
tests go in the same files as the code, you’ll use <code>#[cfg(test)]</code> to specify
|
||||
that they shouldn’t be included in the compiled result.</p>
|
||||
<p>Recall that when we generated the new <code>adder</code> project in the first section of
|
||||
this chapter, Cargo generated this code for us:</p>
|
||||
<p><span class="filename">Filename: src/lib.rs</span></p>
|
||||
<pre><code class="language-rust noplayground">pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}</code></pre>
|
||||
<p>On the automatically generated <code>tests</code> module, the attribute <code>cfg</code> stands for
|
||||
<em>configuration</em> and tells Rust that the following item should only be included
|
||||
given a certain configuration option. In this case, the configuration option is
|
||||
<code>test</code>, which is provided by Rust for compiling and running tests. By using the
|
||||
<code>cfg</code> attribute, Cargo compiles our test code only if we actively run the tests
|
||||
with <code>cargo test</code>. This includes any helper functions that might be within this
|
||||
module, in addition to the functions annotated with <code>#[test]</code>.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="testing-private-functions"></a></p>
|
||||
<h4 id="private-function-tests"><a class="header" href="#private-function-tests">Private Function Tests</a></h4>
|
||||
<p>There’s debate within the testing community about whether or not private
|
||||
functions should be tested directly, and other languages make it difficult or
|
||||
impossible to test private functions. Regardless of which testing ideology you
|
||||
adhere to, Rust’s privacy rules do allow you to test private functions.
|
||||
Consider the code in Listing 11-12 with the private function <code>internal_adder</code>.</p>
|
||||
<figure class="listing" id="listing-11-12">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust noplayground">pub fn add_two(a: u64) -> u64 {
|
||||
internal_adder(a, 2)
|
||||
}
|
||||
|
||||
fn internal_adder(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn internal() {
|
||||
let result = internal_adder(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-12">Listing 11-12</a>: Testing a private function</figcaption>
|
||||
</figure>
|
||||
<p>Note that the <code>internal_adder</code> function is not marked as <code>pub</code>. Tests are just
|
||||
Rust code, and the <code>tests</code> module is just another module. As we discussed in
|
||||
<a href="../ch07/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html">“Paths for Referring to an Item in the Module Tree”</a><!-- ignore -->,
|
||||
items in child modules can use the items in their ancestor modules. In this
|
||||
test, we bring all of the items belonging to the <code>tests</code> module’s parent into
|
||||
scope with <code>use super::*</code>, and then the test can call <code>internal_adder</code>. If you
|
||||
don’t think private functions should be tested, there’s nothing in Rust that
|
||||
will compel you to do so.</p>
|
||||
<h3 id="integration-tests"><a class="header" href="#integration-tests">Integration Tests</a></h3>
|
||||
<p>In Rust, integration tests are entirely external to your library. They use your
|
||||
library in the same way any other code would, which means they can only call
|
||||
functions that are part of your library’s public API. Their purpose is to test
|
||||
whether many parts of your library work together correctly. Units of code that
|
||||
work correctly on their own could have problems when integrated, so test
|
||||
coverage of the integrated code is important as well. To create integration
|
||||
tests, you first need a <em>tests</em> directory.</p>
|
||||
<h4 id="the-tests-directory"><a class="header" href="#the-tests-directory">The <em>tests</em> Directory</a></h4>
|
||||
<p>We create a <em>tests</em> directory at the top level of our project directory, next
|
||||
to <em>src</em>. Cargo knows to look for integration test files in this directory. We
|
||||
can then make as many test files as we want, and Cargo will compile each of the
|
||||
files as an individual crate.</p>
|
||||
<p>Let’s create an integration test. With the code in Listing 11-12 still in the
|
||||
<em>src/lib.rs</em> file, make a <em>tests</em> directory, and create a new file named
|
||||
<em>tests/integration_test.rs</em>. Your directory structure should look like this:</p>
|
||||
<pre><code class="language-text">adder
|
||||
├── Cargo.lock
|
||||
├── Cargo.toml
|
||||
├── src
|
||||
│ └── lib.rs
|
||||
└── tests
|
||||
└── integration_test.rs
|
||||
</code></pre>
|
||||
<p>Enter the code in Listing 11-13 into the <em>tests/integration_test.rs</em> file.</p>
|
||||
<figure class="listing" id="listing-11-13">
|
||||
<span class="file-name">Filename: tests/integration_test.rs</span>
|
||||
<pre><code class="language-rust ignore">use adder::add_two;
|
||||
|
||||
#[test]
|
||||
fn it_adds_two() {
|
||||
let result = add_two(2);
|
||||
assert_eq!(result, 4);
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-11-13">Listing 11-13</a>: An integration test of a function in the <code>adder</code> crate</figcaption>
|
||||
</figure>
|
||||
<p>Each file in the <em>tests</em> directory is a separate crate, so we need to bring our
|
||||
library into each test crate’s scope. For that reason, we add <code>use adder::add_two;</code> at the top of the code, which we didn’t need in the unit tests.</p>
|
||||
<p>We don’t need to annotate any code in <em>tests/integration_test.rs</em> with
|
||||
<code>#[cfg(test)]</code>. Cargo treats the <em>tests</em> directory specially and compiles files
|
||||
in this directory only when we run <code>cargo test</code>. Run <code>cargo test</code> now:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 1.31s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-1082c4b063a8fbe6)
|
||||
|
||||
running 1 test
|
||||
test tests::internal ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Running tests/integration_test.rs (target/debug/deps/integration_test-1082c4b063a8fbe6)
|
||||
|
||||
running 1 test
|
||||
test it_adds_two ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests adder
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>The three sections of output include the unit tests, the integration test, and
|
||||
the doc tests. Note that if any test in a section fails, the following sections
|
||||
will not be run. For example, if a unit test fails, there won’t be any output
|
||||
for integration and doc tests, because those tests will only be run if all unit
|
||||
tests are passing.</p>
|
||||
<p>The first section for the unit tests is the same as we’ve been seeing: one line
|
||||
for each unit test (one named <code>internal</code> that we added in Listing 11-12) and
|
||||
then a summary line for the unit tests.</p>
|
||||
<p>The integration tests section starts with the line <code>Running tests/integration_test.rs</code>. Next, there is a line for each test function in
|
||||
that integration test and a summary line for the results of the integration
|
||||
test just before the <code>Doc-tests adder</code> section starts.</p>
|
||||
<p>Each integration test file has its own section, so if we add more files in the
|
||||
<em>tests</em> directory, there will be more integration test sections.</p>
|
||||
<p>We can still run a particular integration test function by specifying the test
|
||||
function’s name as an argument to <code>cargo test</code>. To run all the tests in a
|
||||
particular integration test file, use the <code>--test</code> argument of <code>cargo test</code>
|
||||
followed by the name of the file:</p>
|
||||
<pre><code class="language-console">$ cargo test --test integration_test
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.64s
|
||||
Running tests/integration_test.rs (target/debug/deps/integration_test-82e7799c1bc62298)
|
||||
|
||||
running 1 test
|
||||
test it_adds_two ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>This command runs only the tests in the <em>tests/integration_test.rs</em> file.</p>
|
||||
<h4 id="submodules-in-integration-tests"><a class="header" href="#submodules-in-integration-tests">Submodules in Integration Tests</a></h4>
|
||||
<p>As you add more integration tests, you might want to make more files in the
|
||||
<em>tests</em> directory to help organize them; for example, you can group the test
|
||||
functions by the functionality they’re testing. As mentioned earlier, each file
|
||||
in the <em>tests</em> directory is compiled as its own separate crate, which is useful
|
||||
for creating separate scopes to more closely imitate the way end users will be
|
||||
using your crate. However, this means files in the <em>tests</em> directory don’t
|
||||
share the same behavior as files in <em>src</em> do, as you learned in Chapter 7
|
||||
regarding how to separate code into modules and files.</p>
|
||||
<p>The different behavior of <em>tests</em> directory files is most noticeable when you
|
||||
have a set of helper functions to use in multiple integration test files, and
|
||||
you try to follow the steps in the <a href="../ch07/ch07-05-separating-modules-into-different-files.html">“Separating Modules into Different
|
||||
Files”</a><!-- ignore --> section of Chapter 7 to
|
||||
extract them into a common module. For example, if we create <em>tests/common.rs</em>
|
||||
and place a function named <code>setup</code> in it, we can add some code to <code>setup</code> that
|
||||
we want to call from multiple test functions in multiple test files:</p>
|
||||
<p><span class="filename">Filename: tests/common.rs</span></p>
|
||||
<pre><code class="language-rust noplayground">pub fn setup() {
|
||||
// setup code specific to your library's tests would go here
|
||||
}</code></pre>
|
||||
<p>When we run the tests again, we’ll see a new section in the test output for the
|
||||
<em>common.rs</em> file, even though this file doesn’t contain any test functions nor
|
||||
did we call the <code>setup</code> function from anywhere:</p>
|
||||
<pre><code class="language-console">$ cargo test
|
||||
Compiling adder v0.1.0 (file:///projects/adder)
|
||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.89s
|
||||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||||
|
||||
running 1 test
|
||||
test tests::internal ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Running tests/common.rs (target/debug/deps/common-92948b65e88960b4)
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Running tests/integration_test.rs (target/debug/deps/integration_test-92948b65e88960b4)
|
||||
|
||||
running 1 test
|
||||
test it_adds_two ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
Doc-tests adder
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
|
||||
</code></pre>
|
||||
<p>Having <code>common</code> appear in the test results with <code>running 0 tests</code> displayed for
|
||||
it is not what we wanted. We just wanted to share some code with the other
|
||||
integration test files. To avoid having <code>common</code> appear in the test output,
|
||||
instead of creating <em>tests/common.rs</em>, we’ll create <em>tests/common/mod.rs</em>. The
|
||||
project directory now looks like this:</p>
|
||||
<pre><code class="language-text">├── Cargo.lock
|
||||
├── Cargo.toml
|
||||
├── src
|
||||
│ └── lib.rs
|
||||
└── tests
|
||||
├── common
|
||||
│ └── mod.rs
|
||||
└── integration_test.rs
|
||||
</code></pre>
|
||||
<p>This is the older naming convention that Rust also understands that we mentioned
|
||||
in <a href="../ch07/ch07-05-separating-modules-into-different-files.html#alternate-file-paths">“Alternate File Paths”</a><!-- ignore --> in Chapter 7. Naming the
|
||||
file this way tells Rust not to treat the <code>common</code> module as an integration test
|
||||
file. When we move the <code>setup</code> function code into <em>tests/common/mod.rs</em> and
|
||||
delete the <em>tests/common.rs</em> file, the section in the test output will no longer
|
||||
appear. Files in subdirectories of the <em>tests</em> directory don’t get compiled as
|
||||
separate crates or have sections in the test output.</p>
|
||||
<p>After we’ve created <em>tests/common/mod.rs</em>, we can use it from any of the
|
||||
integration test files as a module. Here’s an example of calling the <code>setup</code>
|
||||
function from the <code>it_adds_two</code> test in <em>tests/integration_test.rs</em>:</p>
|
||||
<p><span class="filename">Filename: tests/integration_test.rs</span></p>
|
||||
<pre><code class="language-rust ignore">use adder::add_two;
|
||||
|
||||
mod common;
|
||||
|
||||
#[test]
|
||||
fn it_adds_two() {
|
||||
common::setup();
|
||||
|
||||
let result = add_two(2);
|
||||
assert_eq!(result, 4);
|
||||
}</code></pre>
|
||||
<p>Note that the <code>mod common;</code> declaration is the same as the module declaration
|
||||
we demonstrated in Listing 7-21. Then, in the test function, we can call the
|
||||
<code>common::setup()</code> function.</p>
|
||||
<h4 id="integration-tests-for-binary-crates"><a class="header" href="#integration-tests-for-binary-crates">Integration Tests for Binary Crates</a></h4>
|
||||
<p>If our project is a binary crate that only contains a <em>src/main.rs</em> file and
|
||||
doesn’t have a <em>src/lib.rs</em> file, we can’t create integration tests in the
|
||||
<em>tests</em> directory and bring functions defined in the <em>src/main.rs</em> file into
|
||||
scope with a <code>use</code> statement. Only library crates expose functions that other
|
||||
crates can use; binary crates are meant to be run on their own.</p>
|
||||
<p>This is one of the reasons Rust projects that provide a binary have a
|
||||
straightforward <em>src/main.rs</em> file that calls logic that lives in the
|
||||
<em>src/lib.rs</em> file. Using that structure, integration tests <em>can</em> test the
|
||||
library crate with <code>use</code> to make the important functionality available. If the
|
||||
important functionality works, the small amount of code in the <em>src/main.rs</em>
|
||||
file will work as well, and that small amount of code doesn’t need to be tested.</p>
|
||||
<h2 id="summary"><a class="header" href="#summary">Summary</a></h2>
|
||||
<p>Rust’s testing features provide a way to specify how code should function to
|
||||
ensure that it continues to work as you expect, even as you make changes. Unit
|
||||
tests exercise different parts of a library separately and can test private
|
||||
implementation details. Integration tests check that many parts of the library
|
||||
work together correctly, and they use the library’s public API to test the code
|
||||
in the same way external code will use it. Even though Rust’s type system and
|
||||
ownership rules help prevent some kinds of bugs, tests are still important to
|
||||
reduce logic bugs having to do with how your code is expected to behave.</p>
|
||||
<p>Let’s combine the knowledge you learned in this chapter and in previous
|
||||
chapters to work on a project!</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user