]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/src/ch11-01-writing-tests.md
New upstream version 1.23.0+dfsg1
[rustc.git] / src / doc / book / second-edition / src / ch11-01-writing-tests.md
CommitLineData
cc61c64b
XL
1## How to Write Tests
2
3b2f2976
XL
3Tests are Rust functions that verify that the non-test code is functioning in
4the expected manner. The bodies of test functions typically perform some setup,
5run the code we want to test, then assert whether the results are what we
6expect. Let’s look at the features Rust provides specifically for writing
7tests: the `test` attribute, a few macros, and the `should_panic` attribute.
cc61c64b
XL
8
9### The Anatomy of a Test Function
10
041b39d2 11At its simplest, a test in Rust is a function that’s annotated with the `test`
cc61c64b
XL
12attribute. Attributes are metadata about pieces of Rust code: the `derive`
13attribute that we used with structs in Chapter 5 is one example. To make a
14function into a test function, we add `#[test]` on the line before `fn`. When
15we run our tests with the `cargo test` command, Rust will build a test runner
16binary that runs the functions annotated with the `test` attribute and reports
17on whether each test function passes or fails.
18
cc61c64b
XL
19We saw in Chapter 7 that when you make a new library project with Cargo, a test
20module with a test function in it is automatically generated for us. This is to
3b2f2976 21help us get started writing our tests so we don’t have to go look up the
cc61c64b
XL
22exact structure and syntax of test functions every time we start a new project.
23We can add as many additional test functions and as many test modules as we
24want, though!
25
041b39d2
XL
26We’re going to explore some aspects of how tests work by experimenting with the
27template test generated for us, without actually testing any code. Then we’ll
28write some real-world tests that call some code that we’ve written and assert
cc61c64b
XL
29that its behavior is correct.
30
041b39d2 31Let’s create a new library project called `adder`:
cc61c64b
XL
32
33```text
34$ cargo new adder
35 Created library `adder` project
36$ cd adder
37```
38
39The contents of the `src/lib.rs` file in your adder library should be as
40follows:
41
42<span class="filename">Filename: src/lib.rs</span>
43
44```rust
45#[cfg(test)]
46mod tests {
47 #[test]
48 fn it_works() {
abe05a73 49 assert_eq!(2 + 2, 4);
cc61c64b
XL
50 }
51}
52```
53
54<span class="caption">Listing 11-1: The test module and function generated
3b2f2976 55automatically for us by `cargo new`</span>
cc61c64b 56
041b39d2 57For now, let’s ignore the top two lines and focus on the function to see how it
cc61c64b
XL
58works. Note the `#[test]` annotation before the `fn` line: this attribute
59indicates this is a test function, so that the test runner knows to treat this
60function as a test. We could also have non-test functions in the `tests` module
61to help set up common scenarios or perform common operations, so we need to
62indicate which functions are tests with the `#[test]` attribute.
63
abe05a73
XL
64The function body uses the `assert_eq!` macro to assert that 2 + 2 equals 4.
65This assertion serves as an example of the format for a typical test. Let’s run
66it and see that this test passes.
cc61c64b
XL
67
68The `cargo test` command runs all tests we have in our project, as shown in
69Listing 11-2:
70
71```text
72$ cargo test
73 Compiling adder v0.1.0 (file:///projects/adder)
7cac9316 74 Finished dev [unoptimized + debuginfo] target(s) in 0.22 secs
cc61c64b
XL
75 Running target/debug/deps/adder-ce99bcc2479f4607
76
77running 1 test
78test tests::it_works ... ok
79
80test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
81
82 Doc-tests adder
83
84running 0 tests
85
86test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
87```
88
89<span class="caption">Listing 11-2: The output from running the one
3b2f2976 90automatically generated test</span>
cc61c64b
XL
91
92Cargo compiled and ran our test. After the `Compiling`, `Finished`, and
93`Running` lines, we see the line `running 1 test`. The next line shows the name
94of the generated test function, called `it_works`, and the result of running
95that test, `ok`. Then we see the overall summary of running the tests: `test
96result: ok.` means all the tests passed. `1 passed; 0 failed` adds up the
97number of tests that passed or failed.
98
041b39d2
XL
99We don’t have any tests we’ve marked as ignored, so the summary says `0
100ignored`. We’re going to talk about ignoring tests in the next section on
cc61c64b
XL
101different ways to run tests. The `0 measured` statistic is for benchmark tests
102that measure performance. Benchmark tests are, as of this writing, only
abe05a73 103available in nightly Rust. See Chapter 1 for more information about nightly
cc61c64b
XL
104Rust.
105
106The next part of the test output that starts with `Doc-tests adder` is for the
041b39d2 107results of any documentation tests. We don’t have any documentation tests yet,
cc61c64b 108but Rust can compile any code examples that appear in our API documentation.
041b39d2
XL
109This feature helps us keep our docs and our code in sync! We’ll be talking
110about how to write documentation tests in the “Documentation Comments” section
111of Chapter 14. We’re going to ignore the `Doc-tests` output for now.
112
113Let’s change the name of our test and see how that changes the test output.
cc61c64b
XL
114Give the `it_works` function a different name, such as `exploration`, like so:
115
116<span class="filename">Filename: src/lib.rs</span>
117
118```rust
119#[cfg(test)]
120mod tests {
121 #[test]
122 fn exploration() {
abe05a73 123 assert_eq!(2 + 2, 4);
cc61c64b
XL
124 }
125}
126```
127
041b39d2 128And run `cargo test` again. In the output, we’ll now see `exploration` instead
cc61c64b
XL
129of `it_works`:
130
131```text
132running 1 test
133test tests::exploration ... ok
134
135test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
136```
137
041b39d2 138Let’s add another test, but this time we’ll make a test that fails! Tests fail
3b2f2976
XL
139when something in the test function panics. Each test is run in a new thread,
140and when the main thread sees that a test thread has died, the test is marked
141as failed. We talked about the simplest way to cause a panic in Chapter 9: call
142the `panic!` macro! Type in the new test so that your `src/lib.rs` now looks
143like Listing 11-3:
cc61c64b
XL
144
145<span class="filename">Filename: src/lib.rs</span>
146
147```rust
148#[cfg(test)]
149mod tests {
150 #[test]
151 fn exploration() {
abe05a73 152 assert_eq!(2 + 2, 4);
cc61c64b
XL
153 }
154
155 #[test]
156 fn another() {
157 panic!("Make this test fail");
158 }
159}
160```
161
162<span class="caption">Listing 11-3: Adding a second test; one that will fail
3b2f2976 163since we call the `panic!` macro</span>
cc61c64b
XL
164
165And run the tests again with `cargo test`. The output should look like Listing
16611-4, which shows that our `exploration` test passed and `another` failed:
167
168```text
169running 2 tests
170test tests::exploration ... ok
171test tests::another ... FAILED
172
173failures:
174
175---- tests::another stdout ----
176 thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:9
177note: Run with `RUST_BACKTRACE=1` for a backtrace.
178
179failures:
180 tests::another
181
182test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured
183
184error: test failed
185```
186
187<span class="caption">Listing 11-4: Test results when one test passes and one
3b2f2976 188test fails</span>
cc61c64b
XL
189
190Instead of `ok`, the line `test tests::another` says `FAILED`. We have two new
191sections between the individual results and the summary: the first section
192displays the detailed reason for the test failures. In this case, `another`
193failed because it `panicked at 'Make this test fail'`, which happened on
194*src/lib.rs* line 9. The next section lists just the names of all the failing
195tests, which is useful when there are lots of tests and lots of detailed
196failing test output. We can use the name of a failing test to run just that
041b39d2 197test in order to more easily debug it; we’ll talk more about ways to run tests
cc61c64b
XL
198in the next section.
199
200Finally, we have the summary line: overall, our test result is `FAILED`. We had
2011 test pass and 1 test fail.
202
041b39d2
XL
203Now that we’ve seen what the test results look like in different scenarios,
204let’s look at some macros other than `panic!` that are useful in tests.
cc61c64b
XL
205
206### Checking Results with the `assert!` Macro
207
208The `assert!` macro, provided by the standard library, is useful when you want
209to ensure that some condition in a test evaluates to `true`. We give the
210`assert!` macro an argument that evaluates to a boolean. If the value is `true`,
211`assert!` does nothing and the test passes. If the value is `false`, `assert!`
212calls the `panic!` macro, which causes the test to fail. This is one macro that
213helps us check that our code is functioning in the way we intend.
214
cc61c64b 215Remember all the way back in Chapter 5, Listing 5-9, where we had a `Rectangle`
041b39d2 216struct and a `can_hold` method, repeated here in Listing 11-5. Let’s put this
ea8adc8c 217code in *src/lib.rs* and write some tests for it using the `assert!` macro.
cc61c64b 218
cc61c64b
XL
219<span class="filename">Filename: src/lib.rs</span>
220
221```rust
222#[derive(Debug)]
223pub struct Rectangle {
224 length: u32,
225 width: u32,
226}
227
228impl Rectangle {
229 pub fn can_hold(&self, other: &Rectangle) -> bool {
230 self.length > other.length && self.width > other.width
231 }
232}
233```
234
235<span class="caption">Listing 11-5: The `Rectangle` struct and its `can_hold`
3b2f2976 236method from Chapter 5</span>
cc61c64b 237
041b39d2
XL
238The `can_hold` method returns a boolean, which means it’s a perfect use case
239for the `assert!` macro. In Listing 11-6, let’s write a test that exercises the
cc61c64b
XL
240`can_hold` method by creating a `Rectangle` instance that has a length of 8 and
241a width of 7, and asserting that it can hold another `Rectangle` instance that
242has a length of 5 and a width of 1:
243
244<span class="filename">Filename: src/lib.rs</span>
245
246```rust
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn larger_can_hold_smaller() {
253 let larger = Rectangle { length: 8, width: 7 };
254 let smaller = Rectangle { length: 5, width: 1 };
255
256 assert!(larger.can_hold(&smaller));
257 }
258}
259```
260
261<span class="caption">Listing 11-6: A test for `can_hold` that checks that a
3b2f2976 262larger rectangle indeed holds a smaller rectangle</span>
cc61c64b 263
041b39d2 264Note that we’ve added a new line inside the `tests` module: `use super::*;`.
cc61c64b 265The `tests` module is a regular module that follows the usual visibility rules
041b39d2
XL
266we covered in Chapter 7. Because we’re in an inner module, we need to bring the
267code under test in the outer module into the scope of the inner module. We’ve
cc61c64b
XL
268chosen to use a glob here so that anything we define in the outer module is
269available to this `tests` module.
270
041b39d2 271We’ve named our test `larger_can_hold_smaller`, and we’ve created the two
cc61c64b
XL
272`Rectangle` instances that we need. Then we called the `assert!` macro and
273passed it the result of calling `larger.can_hold(&smaller)`. This expression is
041b39d2 274supposed to return `true`, so our test should pass. Let’s find out!
cc61c64b
XL
275
276```text
277running 1 test
278test tests::larger_can_hold_smaller ... ok
279
280test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
281```
282
041b39d2 283It does pass! Let’s add another test, this time asserting that a smaller
cc61c64b
XL
284rectangle cannot hold a larger rectangle:
285
286<span class="filename">Filename: src/lib.rs</span>
287
288```rust
289#[cfg(test)]
290mod tests {
291 use super::*;
292
293 #[test]
294 fn larger_can_hold_smaller() {
295 let larger = Rectangle { length: 8, width: 7 };
296 let smaller = Rectangle { length: 5, width: 1 };
297
298 assert!(larger.can_hold(&smaller));
299 }
300
301 #[test]
041b39d2 302 fn smaller_cannot_hold_larger() {
cc61c64b
XL
303 let larger = Rectangle { length: 8, width: 7 };
304 let smaller = Rectangle { length: 5, width: 1 };
305
306 assert!(!smaller.can_hold(&larger));
307 }
308}
309```
310
311Because the correct result of the `can_hold` function in this case is `false`,
312we need to negate that result before we pass it to the `assert!` macro. This
313way, our test will pass if `can_hold` returns `false`:
314
315```text
316running 2 tests
041b39d2 317test tests::smaller_cannot_hold_larger ... ok
cc61c64b
XL
318test tests::larger_can_hold_smaller ... ok
319
320test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured
321```
322
041b39d2
XL
323Two passing tests! Now let’s see what happens to our test results if we
324introduce a bug in our code. Let’s change the implementation of the `can_hold`
325method to have a less-than sign when it compares the lengths where it’s
cc61c64b
XL
326supposed to have a greater-than sign:
327
328```rust
329#[derive(Debug)]
330pub struct Rectangle {
331 length: u32,
332 width: u32,
333}
334
335impl Rectangle {
336 pub fn can_hold(&self, other: &Rectangle) -> bool {
337 self.length < other.length && self.width > other.width
338 }
339}
340```
341
342Running the tests now produces:
343
344```text
345running 2 tests
041b39d2 346test tests::smaller_cannot_hold_larger ... ok
cc61c64b
XL
347test tests::larger_can_hold_smaller ... FAILED
348
349failures:
350
351---- tests::larger_can_hold_smaller stdout ----
352 thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed:
353 larger.can_hold(&smaller)', src/lib.rs:22
354note: Run with `RUST_BACKTRACE=1` for a backtrace.
355
356failures:
357 tests::larger_can_hold_smaller
358
359test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured
360```
361
362Our tests caught the bug! Since `larger.length` is 8 and `smaller.length` is 5,
363the comparison of the lengths in `can_hold` now returns `false` since 8 is not
364less than 5.
365
366### Testing Equality with the `assert_eq!` and `assert_ne!` Macros
367
368A common way to test functionality is to take the result of the code under test
041b39d2 369and the value we expect the code to return and check that they’re equal. We
cc61c64b
XL
370could do this using the `assert!` macro and passing it an expression using the
371`==` operator. However, this is such a common test that the standard library
372provides a pair of macros to perform this test more conveniently: `assert_eq!`
373and `assert_ne!`. These macros compare two arguments for equality or
041b39d2
XL
374inequality, respectively. They’ll also print out the two values if the
375assertion fails, so that it’s easier to see *why* the test failed, while the
cc61c64b
XL
376`assert!` macro only tells us that it got a `false` value for the `==`
377expression, not the values that lead to the `false` value.
378
041b39d2
XL
379In Listing 11-7, let’s write a function named `add_two` that adds two to its
380parameter and returns the result. Then let’s test this function using the
cc61c64b
XL
381`assert_eq!` macro:
382
383<span class="filename">Filename: src/lib.rs</span>
384
385```rust
386pub fn add_two(a: i32) -> i32 {
387 a + 2
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn it_adds_two() {
396 assert_eq!(4, add_two(2));
397 }
398}
399```
400
401<span class="caption">Listing 11-7: Testing the function `add_two` using the
3b2f2976 402`assert_eq!` macro</span>
cc61c64b 403
041b39d2 404Let’s check that it passes!
cc61c64b
XL
405
406```text
407running 1 test
408test tests::it_adds_two ... ok
409
410test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
411```
412
413The first argument we gave to the `assert_eq!` macro, 4, is equal to the result
414of calling `add_two(2)`. We see a line for this test that says `test
415tests::it_adds_two ... ok`, and the `ok` text indicates that our test passed!
416
041b39d2 417Let’s introduce a bug into our code to see what it looks like when a test that
cc61c64b
XL
418uses `assert_eq!` fails. Change the implementation of the `add_two` function to
419instead add 3:
420
421```rust
422pub fn add_two(a: i32) -> i32 {
423 a + 3
424}
425```
426
427And run the tests again:
428
429```text
430running 1 test
431test tests::it_adds_two ... FAILED
432
433failures:
434
435---- tests::it_adds_two stdout ----
436 thread 'tests::it_adds_two' panicked at 'assertion failed: `(left ==
437 right)` (left: `4`, right: `5`)', src/lib.rs:11
438note: Run with `RUST_BACKTRACE=1` for a backtrace.
439
440failures:
441 tests::it_adds_two
442
443test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
444```
445
446Our test caught the bug! The `it_adds_two` test failed with the message ``
447assertion failed: `(left == right)` (left: `4`, right: `5`) ``. This message is
448useful and helps us get started debugging: it says the `left` argument to
449`assert_eq!` was 4, but the `right` argument, where we had `add_two(2)`, was 5.
450
451Note that in some languages and test frameworks, the parameters to the
452functions that assert two values are equal are called `expected` and `actual`
453and the order in which we specify the arguments matters. However, in Rust,
041b39d2
XL
454they’re called `left` and `right` instead, and the order in which we specify
455the value we expect and the value that the code under test produces doesn’t
3b2f2976 456matter. We could write the assertion in this test as
cc61c64b
XL
457`assert_eq!(add_two(2), 4)`, which would result in a failure message that says
458`` assertion failed: `(left == right)` (left: `5`, right: `4`) ``.
459
460The `assert_ne!` macro will pass if the two values we give to it are not equal
041b39d2 461and fail if they are equal. This macro is most useful for cases when we’re not
cc61c64b 462sure exactly what a value *will* be, but we know what the value definitely
041b39d2 463*won’t* be, if our code is functioning as we intend. For example, if we have a
cc61c64b
XL
464function that is guaranteed to change its input in some way, but the way in
465which the input is changed depends on the day of the week that we run our
466tests, the best thing to assert might be that the output of the function is not
467equal to the input.
468
469Under the surface, the `assert_eq!` and `assert_ne!` macros use the operators
470`==` and `!=`, respectively. When the assertions fail, these macros print their
471arguments using debug formatting, which means the values being compared must
472implement the `PartialEq` and `Debug` traits. All of the primitive types and
473most of the standard library types implement these traits. For structs and
041b39d2
XL
474enums that you define, you’ll need to implement `PartialEq` in order to be able
475to assert that values of those types are equal or not equal. You’ll need to
cc61c64b
XL
476implement `Debug` in order to be able to print out the values in the case that
477the assertion fails. Because both of these traits are derivable traits, as we
478mentioned in Chapter 5, this is usually as straightforward as adding the
479`#[derive(PartialEq, Debug)]` annotation to your struct or enum definition. See
480Appendix C for more details about these and other derivable traits.
481
482### Custom Failure Messages
483
484We can also add a custom message to be printed with the failure message as
485optional arguments to `assert!`, `assert_eq!`, and `assert_ne!`. Any arguments
486specified after the one required argument to `assert!` or the two required
487arguments to `assert_eq!` and `assert_ne!` are passed along to the `format!`
488macro that we talked about in Chapter 8, so you can pass a format string that
489contains `{}` placeholders and values to go in the placeholders. Custom
490messages are useful in order to document what an assertion means, so that when
491the test fails, we have a better idea of what the problem is with the code.
492
041b39d2 493For example, let’s say we have a function that greets people by name, and we
cc61c64b
XL
494want to test that the name we pass into the function appears in the output:
495
496<span class="filename">Filename: src/lib.rs</span>
497
498```rust
499pub fn greeting(name: &str) -> String {
500 format!("Hello {}!", name)
501}
502
503#[cfg(test)]
504mod tests {
505 use super::*;
506
507 #[test]
508 fn greeting_contains_name() {
509 let result = greeting("Carol");
510 assert!(result.contains("Carol"));
511 }
512}
513```
514
041b39d2 515The requirements for this program haven’t been agreed upon yet, and we’re
cc61c64b 516pretty sure the `Hello` text at the beginning of the greeting will change. We
041b39d2 517decided we don’t want to have to update the test for the name when that
cc61c64b 518happens, so instead of checking for exact equality to the value returned from
041b39d2 519the `greeting` function, we’re just going to assert that the output contains
cc61c64b
XL
520the text of the input parameter.
521
041b39d2 522Let’s introduce a bug into this code to see what this test failure looks like,
cc61c64b
XL
523by changing `greeting` to not include `name`:
524
525```rust
526pub fn greeting(name: &str) -> String {
527 String::from("Hello!")
528}
529```
530
531Running this test produces:
532
533```text
534running 1 test
535test tests::greeting_contains_name ... FAILED
536
537failures:
538
539---- tests::greeting_contains_name stdout ----
540 thread 'tests::greeting_contains_name' panicked at 'assertion failed:
541 result.contains("Carol")', src/lib.rs:12
542note: Run with `RUST_BACKTRACE=1` for a backtrace.
543
544failures:
545 tests::greeting_contains_name
546```
547
548This just tells us that the assertion failed and which line the assertion is
549on. A more useful failure message in this case would print the value we did get
041b39d2 550from the `greeting` function. Let’s change the test function to have a custom
cc61c64b
XL
551failure message made from a format string with a placeholder filled in with the
552actual value we got from the `greeting` function:
553
554```rust,ignore
555#[test]
556fn greeting_contains_name() {
557 let result = greeting("Carol");
558 assert!(
559 result.contains("Carol"),
560 "Greeting did not contain name, value was `{}`", result
561 );
562}
563```
564
041b39d2 565Now if we run the test again, we’ll get a much more informative error message:
cc61c64b
XL
566
567```text
568---- tests::greeting_contains_name stdout ----
7cac9316 569 thread 'tests::greeting_contains_name' panicked at 'Greeting did not contain
cc61c64b
XL
570 name, value was `Hello`', src/lib.rs:12
571note: Run with `RUST_BACKTRACE=1` for a backtrace.
572```
573
574We can see the value we actually got in the test output, which would help us
575debug what happened instead of what we were expecting to happen.
576
577### Checking for Panics with `should_panic`
578
579In addition to checking that our code returns the correct values we expect,
041b39d2 580it’s also important to check that our code handles error conditions as we
cc61c64b
XL
581expect. For example, consider the `Guess` type that we created in Chapter 9 in
582Listing 9-8. Other code that uses `Guess` is depending on the guarantee that
583`Guess` instances will only contain values between 1 and 100. We can write a
584test that ensures that attempting to create a `Guess` instance with a value
585outside that range panics.
586
587We can do this by adding another attribute, `should_panic`, to our test
588function. This attribute makes a test pass if the code inside the function
3b2f2976 589panics, and the test will fail if the code inside the function doesn’t panic.
cc61c64b 590
041b39d2 591Listing 11-8 shows how we’d write a test that checks the error conditions of
cc61c64b
XL
592`Guess::new` happen when we expect:
593
594<span class="filename">Filename: src/lib.rs</span>
595
596```rust
ea8adc8c 597pub struct Guess {
cc61c64b
XL
598 value: u32,
599}
600
601impl Guess {
602 pub fn new(value: u32) -> Guess {
603 if value < 1 || value > 100 {
604 panic!("Guess value must be between 1 and 100, got {}.", value);
605 }
606
607 Guess {
7cac9316 608 value
cc61c64b
XL
609 }
610 }
611}
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616
617 #[test]
618 #[should_panic]
619 fn greater_than_100() {
620 Guess::new(200);
621 }
622}
623```
624
625<span class="caption">Listing 11-8: Testing that a condition will cause a
3b2f2976 626`panic!`</span>
cc61c64b
XL
627
628The `#[should_panic]` attribute goes after the `#[test]` attribute and before
041b39d2 629the test function it applies to. Let’s see what it looks like when this test
cc61c64b
XL
630passes:
631
632```text
633running 1 test
634test tests::greater_than_100 ... ok
635
636test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
637```
638
041b39d2 639Looks good! Now let’s introduce a bug in our code, by removing the condition
cc61c64b
XL
640that the `new` function will panic if the value is greater than 100:
641
642```rust
ea8adc8c 643# pub struct Guess {
cc61c64b
XL
644# value: u32,
645# }
646#
647impl Guess {
648 pub fn new(value: u32) -> Guess {
649 if value < 1 {
650 panic!("Guess value must be between 1 and 100, got {}.", value);
651 }
652
653 Guess {
7cac9316 654 value
cc61c64b
XL
655 }
656 }
657}
658```
659
660If we run the test from Listing 11-8, it will fail:
661
662```text
663running 1 test
664test tests::greater_than_100 ... FAILED
665
666failures:
667
668failures:
669 tests::greater_than_100
670
671test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
672```
673
041b39d2
XL
674We don’t get a very helpful message in this case, but once we look at the test
675function, we can see that it’s annotated with `#[should_panic]`. The failure we
cc61c64b
XL
676got means that the code in the function, `Guess::new(200)`, did not cause a
677panic.
678
679`should_panic` tests can be imprecise, however, because they only tell us that
680the code has caused some panic. A `should_panic` test would pass even if the
681test panics for a different reason than the one we were expecting to happen. To
682make `should_panic` tests more precise, we can add an optional `expected`
683parameter to the `should_panic` attribute. The test harness will make sure that
684the failure message contains the provided text. For example, consider the
685modified code for `Guess` in Listing 11-9 where the `new` function panics with
686different messages depending on whether the value was too small or too large:
687
688<span class="filename">Filename: src/lib.rs</span>
689
690```rust
ea8adc8c 691pub struct Guess {
cc61c64b
XL
692 value: u32,
693}
694
695impl Guess {
696 pub fn new(value: u32) -> Guess {
697 if value < 1 {
698 panic!("Guess value must be greater than or equal to 1, got {}.",
699 value);
700 } else if value > 100 {
701 panic!("Guess value must be less than or equal to 100, got {}.",
702 value);
703 }
704
705 Guess {
7cac9316 706 value
cc61c64b
XL
707 }
708 }
709}
710
711#[cfg(test)]
712mod tests {
713 use super::*;
714
715 #[test]
716 #[should_panic(expected = "Guess value must be less than or equal to 100")]
717 fn greater_than_100() {
718 Guess::new(200);
719 }
720}
721```
722
723<span class="caption">Listing 11-9: Testing that a condition will cause a
3b2f2976 724`panic!` with a particular panic message</span>
cc61c64b
XL
725
726This test will pass, because the value we put in the `expected` parameter of
727the `should_panic` attribute is a substring of the message that the
728`Guess::new` function panics with. We could have specified the whole panic
729message that we expect, which in this case would be `Guess value must be less
730than or equal to 100, got 200.` It depends on how much of the panic message is
731unique or dynamic and how precise you want your test to be. In this case, a
732substring of the panic message is enough to ensure that the code in the
733function that gets run is the `else if value > 100` case.
734
735To see what happens when a `should_panic` test with an `expected` message
041b39d2 736fails, let’s again introduce a bug into our code by swapping the bodies of the
cc61c64b
XL
737`if value < 1` and the `else if value > 100` blocks:
738
739```rust,ignore
740if value < 1 {
741 panic!("Guess value must be less than or equal to 100, got {}.", value);
742} else if value > 100 {
743 panic!("Guess value must be greater than or equal to 1, got {}.", value);
744}
745```
746
747This time when we run the `should_panic` test, it will fail:
748
749```text
750running 1 test
751test tests::greater_than_100 ... FAILED
752
753failures:
754
755---- tests::greater_than_100 stdout ----
756 thread 'tests::greater_than_100' panicked at 'Guess value must be greater
757 than or equal to 1, got 200.', src/lib.rs:10
758note: Run with `RUST_BACKTRACE=1` for a backtrace.
759note: Panic did not include expected string 'Guess value must be less than or
760equal to 100'
761
762failures:
763 tests::greater_than_100
764
765test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
766```
767
768The failure message indicates that this test did indeed panic as we expected,
ea8adc8c 769but the panic message did not include expected string `'Guess value must be
cc61c64b
XL
770less than or equal to 100'`. We can see the panic message that we did get,
771which in this case was `Guess value must be greater than or equal to 1, got
772200.` We could then start figuring out where our bug was!
773
041b39d2 774Now that we’ve gone over ways to write tests, let’s look at what is happening
cc61c64b
XL
775when we run our tests and talk about the different options we can use with
776`cargo test`.