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