feat: added cleanscript
This commit is contained in:
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>
|
||||
Reference in New Issue
Block a user