]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/src/ch11-01-writing-tests.md
New upstream version 1.21.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() {
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
cc61c64b
XL
214code in *src/lib.rs* instead of *src/main.rs* and write some tests for it using
215the `assert!` macro.
216
cc61c64b
XL
217<span class="filename">Filename: src/lib.rs</span>
218
219```rust
220#[derive(Debug)]
221pub struct Rectangle {
222 length: u32,
223 width: u32,
224}
225
226impl Rectangle {
227 pub fn can_hold(&self, other: &Rectangle) -> bool {
228 self.length > other.length && self.width > other.width
229 }
230}
231```
232
233<span class="caption">Listing 11-5: The `Rectangle` struct and its `can_hold`
3b2f2976 234method from Chapter 5</span>
cc61c64b 235
041b39d2
XL
236The `can_hold` method returns a boolean, which means it’s a perfect use case
237for the `assert!` macro. In Listing 11-6, let’s write a test that exercises the
cc61c64b
XL
238`can_hold` method by creating a `Rectangle` instance that has a length of 8 and
239a width of 7, and asserting that it can hold another `Rectangle` instance that
240has a length of 5 and a width of 1:
241
242<span class="filename">Filename: src/lib.rs</span>
243
244```rust
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn larger_can_hold_smaller() {
251 let larger = Rectangle { length: 8, width: 7 };
252 let smaller = Rectangle { length: 5, width: 1 };
253
254 assert!(larger.can_hold(&smaller));
255 }
256}
257```
258
259<span class="caption">Listing 11-6: A test for `can_hold` that checks that a
3b2f2976 260larger rectangle indeed holds a smaller rectangle</span>
cc61c64b 261
041b39d2 262Note that we’ve added a new line inside the `tests` module: `use super::*;`.
cc61c64b 263The `tests` module is a regular module that follows the usual visibility rules
041b39d2
XL
264we covered in Chapter 7. Because we’re in an inner module, we need to bring the
265code under test in the outer module into the scope of the inner module. We’ve
cc61c64b
XL
266chosen to use a glob here so that anything we define in the outer module is
267available to this `tests` module.
268
041b39d2 269We’ve named our test `larger_can_hold_smaller`, and we’ve created the two
cc61c64b
XL
270`Rectangle` instances that we need. Then we called the `assert!` macro and
271passed it the result of calling `larger.can_hold(&smaller)`. This expression is
041b39d2 272supposed to return `true`, so our test should pass. Let’s find out!
cc61c64b
XL
273
274```text
275running 1 test
276test tests::larger_can_hold_smaller ... ok
277
278test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
279```
280
041b39d2 281It does pass! Let’s add another test, this time asserting that a smaller
cc61c64b
XL
282rectangle cannot hold a larger rectangle:
283
284<span class="filename">Filename: src/lib.rs</span>
285
286```rust
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn larger_can_hold_smaller() {
293 let larger = Rectangle { length: 8, width: 7 };
294 let smaller = Rectangle { length: 5, width: 1 };
295
296 assert!(larger.can_hold(&smaller));
297 }
298
299 #[test]
041b39d2 300 fn smaller_cannot_hold_larger() {
cc61c64b
XL
301 let larger = Rectangle { length: 8, width: 7 };
302 let smaller = Rectangle { length: 5, width: 1 };
303
304 assert!(!smaller.can_hold(&larger));
305 }
306}
307```
308
309Because the correct result of the `can_hold` function in this case is `false`,
310we need to negate that result before we pass it to the `assert!` macro. This
311way, our test will pass if `can_hold` returns `false`:
312
313```text
314running 2 tests
041b39d2 315test tests::smaller_cannot_hold_larger ... ok
cc61c64b
XL
316test tests::larger_can_hold_smaller ... ok
317
318test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured
319```
320
041b39d2
XL
321Two passing tests! Now let’s see what happens to our test results if we
322introduce a bug in our code. Let’s change the implementation of the `can_hold`
323method to have a less-than sign when it compares the lengths where it’s
cc61c64b
XL
324supposed to have a greater-than sign:
325
326```rust
327#[derive(Debug)]
328pub struct Rectangle {
329 length: u32,
330 width: u32,
331}
332
333impl Rectangle {
334 pub fn can_hold(&self, other: &Rectangle) -> bool {
335 self.length < other.length && self.width > other.width
336 }
337}
338```
339
340Running the tests now produces:
341
342```text
343running 2 tests
041b39d2 344test tests::smaller_cannot_hold_larger ... ok
cc61c64b
XL
345test tests::larger_can_hold_smaller ... FAILED
346
347failures:
348
349---- tests::larger_can_hold_smaller stdout ----
350 thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed:
351 larger.can_hold(&smaller)', src/lib.rs:22
352note: Run with `RUST_BACKTRACE=1` for a backtrace.
353
354failures:
355 tests::larger_can_hold_smaller
356
357test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured
358```
359
360Our tests caught the bug! Since `larger.length` is 8 and `smaller.length` is 5,
361the comparison of the lengths in `can_hold` now returns `false` since 8 is not
362less than 5.
363
364### Testing Equality with the `assert_eq!` and `assert_ne!` Macros
365
366A common way to test functionality is to take the result of the code under test
041b39d2 367and the value we expect the code to return and check that they’re equal. We
cc61c64b
XL
368could do this using the `assert!` macro and passing it an expression using the
369`==` operator. However, this is such a common test that the standard library
370provides a pair of macros to perform this test more conveniently: `assert_eq!`
371and `assert_ne!`. These macros compare two arguments for equality or
041b39d2
XL
372inequality, respectively. They’ll also print out the two values if the
373assertion fails, so that it’s easier to see *why* the test failed, while the
cc61c64b
XL
374`assert!` macro only tells us that it got a `false` value for the `==`
375expression, not the values that lead to the `false` value.
376
041b39d2
XL
377In Listing 11-7, let’s write a function named `add_two` that adds two to its
378parameter and returns the result. Then let’s test this function using the
cc61c64b
XL
379`assert_eq!` macro:
380
381<span class="filename">Filename: src/lib.rs</span>
382
383```rust
384pub fn add_two(a: i32) -> i32 {
385 a + 2
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
393 fn it_adds_two() {
394 assert_eq!(4, add_two(2));
395 }
396}
397```
398
399<span class="caption">Listing 11-7: Testing the function `add_two` using the
3b2f2976 400`assert_eq!` macro</span>
cc61c64b 401
041b39d2 402Let’s check that it passes!
cc61c64b
XL
403
404```text
405running 1 test
406test tests::it_adds_two ... ok
407
408test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
409```
410
411The first argument we gave to the `assert_eq!` macro, 4, is equal to the result
412of calling `add_two(2)`. We see a line for this test that says `test
413tests::it_adds_two ... ok`, and the `ok` text indicates that our test passed!
414
041b39d2 415Let’s introduce a bug into our code to see what it looks like when a test that
cc61c64b
XL
416uses `assert_eq!` fails. Change the implementation of the `add_two` function to
417instead add 3:
418
419```rust
420pub fn add_two(a: i32) -> i32 {
421 a + 3
422}
423```
424
425And run the tests again:
426
427```text
428running 1 test
429test tests::it_adds_two ... FAILED
430
431failures:
432
433---- tests::it_adds_two stdout ----
434 thread 'tests::it_adds_two' panicked at 'assertion failed: `(left ==
435 right)` (left: `4`, right: `5`)', src/lib.rs:11
436note: Run with `RUST_BACKTRACE=1` for a backtrace.
437
438failures:
439 tests::it_adds_two
440
441test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
442```
443
444Our test caught the bug! The `it_adds_two` test failed with the message ``
445assertion failed: `(left == right)` (left: `4`, right: `5`) ``. This message is
446useful and helps us get started debugging: it says the `left` argument to
447`assert_eq!` was 4, but the `right` argument, where we had `add_two(2)`, was 5.
448
449Note that in some languages and test frameworks, the parameters to the
450functions that assert two values are equal are called `expected` and `actual`
451and the order in which we specify the arguments matters. However, in Rust,
041b39d2
XL
452they’re called `left` and `right` instead, and the order in which we specify
453the value we expect and the value that the code under test produces doesn’t
3b2f2976 454matter. We could write the assertion in this test as
cc61c64b
XL
455`assert_eq!(add_two(2), 4)`, which would result in a failure message that says
456`` assertion failed: `(left == right)` (left: `5`, right: `4`) ``.
457
458The `assert_ne!` macro will pass if the two values we give to it are not equal
041b39d2 459and fail if they are equal. This macro is most useful for cases when we’re not
cc61c64b 460sure exactly what a value *will* be, but we know what the value definitely
041b39d2 461*won’t* be, if our code is functioning as we intend. For example, if we have a
cc61c64b
XL
462function that is guaranteed to change its input in some way, but the way in
463which the input is changed depends on the day of the week that we run our
464tests, the best thing to assert might be that the output of the function is not
465equal to the input.
466
467Under the surface, the `assert_eq!` and `assert_ne!` macros use the operators
468`==` and `!=`, respectively. When the assertions fail, these macros print their
469arguments using debug formatting, which means the values being compared must
470implement the `PartialEq` and `Debug` traits. All of the primitive types and
471most of the standard library types implement these traits. For structs and
041b39d2
XL
472enums that you define, you’ll need to implement `PartialEq` in order to be able
473to assert that values of those types are equal or not equal. You’ll need to
cc61c64b
XL
474implement `Debug` in order to be able to print out the values in the case that
475the assertion fails. Because both of these traits are derivable traits, as we
476mentioned in Chapter 5, this is usually as straightforward as adding the
477`#[derive(PartialEq, Debug)]` annotation to your struct or enum definition. See
478Appendix C for more details about these and other derivable traits.
479
480### Custom Failure Messages
481
482We can also add a custom message to be printed with the failure message as
483optional arguments to `assert!`, `assert_eq!`, and `assert_ne!`. Any arguments
484specified after the one required argument to `assert!` or the two required
485arguments to `assert_eq!` and `assert_ne!` are passed along to the `format!`
486macro that we talked about in Chapter 8, so you can pass a format string that
487contains `{}` placeholders and values to go in the placeholders. Custom
488messages are useful in order to document what an assertion means, so that when
489the test fails, we have a better idea of what the problem is with the code.
490
041b39d2 491For example, let’s say we have a function that greets people by name, and we
cc61c64b
XL
492want to test that the name we pass into the function appears in the output:
493
494<span class="filename">Filename: src/lib.rs</span>
495
496```rust
497pub fn greeting(name: &str) -> String {
498 format!("Hello {}!", name)
499}
500
501#[cfg(test)]
502mod tests {
503 use super::*;
504
505 #[test]
506 fn greeting_contains_name() {
507 let result = greeting("Carol");
508 assert!(result.contains("Carol"));
509 }
510}
511```
512
041b39d2 513The requirements for this program haven’t been agreed upon yet, and we’re
cc61c64b 514pretty sure the `Hello` text at the beginning of the greeting will change. We
041b39d2 515decided we don’t want to have to update the test for the name when that
cc61c64b 516happens, so instead of checking for exact equality to the value returned from
041b39d2 517the `greeting` function, we’re just going to assert that the output contains
cc61c64b
XL
518the text of the input parameter.
519
041b39d2 520Let’s introduce a bug into this code to see what this test failure looks like,
cc61c64b
XL
521by changing `greeting` to not include `name`:
522
523```rust
524pub fn greeting(name: &str) -> String {
525 String::from("Hello!")
526}
527```
528
529Running this test produces:
530
531```text
532running 1 test
533test tests::greeting_contains_name ... FAILED
534
535failures:
536
537---- tests::greeting_contains_name stdout ----
538 thread 'tests::greeting_contains_name' panicked at 'assertion failed:
539 result.contains("Carol")', src/lib.rs:12
540note: Run with `RUST_BACKTRACE=1` for a backtrace.
541
542failures:
543 tests::greeting_contains_name
544```
545
546This just tells us that the assertion failed and which line the assertion is
547on. A more useful failure message in this case would print the value we did get
041b39d2 548from the `greeting` function. Let’s change the test function to have a custom
cc61c64b
XL
549failure message made from a format string with a placeholder filled in with the
550actual value we got from the `greeting` function:
551
552```rust,ignore
553#[test]
554fn greeting_contains_name() {
555 let result = greeting("Carol");
556 assert!(
557 result.contains("Carol"),
558 "Greeting did not contain name, value was `{}`", result
559 );
560}
561```
562
041b39d2 563Now if we run the test again, we’ll get a much more informative error message:
cc61c64b
XL
564
565```text
566---- tests::greeting_contains_name stdout ----
7cac9316 567 thread 'tests::greeting_contains_name' panicked at 'Greeting did not contain
cc61c64b
XL
568 name, value was `Hello`', src/lib.rs:12
569note: Run with `RUST_BACKTRACE=1` for a backtrace.
570```
571
572We can see the value we actually got in the test output, which would help us
573debug what happened instead of what we were expecting to happen.
574
575### Checking for Panics with `should_panic`
576
577In addition to checking that our code returns the correct values we expect,
041b39d2 578it’s also important to check that our code handles error conditions as we
cc61c64b
XL
579expect. For example, consider the `Guess` type that we created in Chapter 9 in
580Listing 9-8. Other code that uses `Guess` is depending on the guarantee that
581`Guess` instances will only contain values between 1 and 100. We can write a
582test that ensures that attempting to create a `Guess` instance with a value
583outside that range panics.
584
585We can do this by adding another attribute, `should_panic`, to our test
586function. This attribute makes a test pass if the code inside the function
3b2f2976 587panics, and the test will fail if the code inside the function doesn’t panic.
cc61c64b 588
041b39d2 589Listing 11-8 shows how we’d write a test that checks the error conditions of
cc61c64b
XL
590`Guess::new` happen when we expect:
591
592<span class="filename">Filename: src/lib.rs</span>
593
594```rust
595struct Guess {
596 value: u32,
597}
598
599impl Guess {
600 pub fn new(value: u32) -> Guess {
601 if value < 1 || value > 100 {
602 panic!("Guess value must be between 1 and 100, got {}.", value);
603 }
604
605 Guess {
7cac9316 606 value
cc61c64b
XL
607 }
608 }
609}
610
611#[cfg(test)]
612mod tests {
613 use super::*;
614
615 #[test]
616 #[should_panic]
617 fn greater_than_100() {
618 Guess::new(200);
619 }
620}
621```
622
623<span class="caption">Listing 11-8: Testing that a condition will cause a
3b2f2976 624`panic!`</span>
cc61c64b
XL
625
626The `#[should_panic]` attribute goes after the `#[test]` attribute and before
041b39d2 627the test function it applies to. Let’s see what it looks like when this test
cc61c64b
XL
628passes:
629
630```text
631running 1 test
632test tests::greater_than_100 ... ok
633
634test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
635```
636
041b39d2 637Looks good! Now let’s introduce a bug in our code, by removing the condition
cc61c64b
XL
638that the `new` function will panic if the value is greater than 100:
639
640```rust
641# struct Guess {
642# value: u32,
643# }
644#
645impl Guess {
646 pub fn new(value: u32) -> Guess {
647 if value < 1 {
648 panic!("Guess value must be between 1 and 100, got {}.", value);
649 }
650
651 Guess {
7cac9316 652 value
cc61c64b
XL
653 }
654 }
655}
656```
657
658If we run the test from Listing 11-8, it will fail:
659
660```text
661running 1 test
662test tests::greater_than_100 ... FAILED
663
664failures:
665
666failures:
667 tests::greater_than_100
668
669test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
670```
671
041b39d2
XL
672We don’t get a very helpful message in this case, but once we look at the test
673function, we can see that it’s annotated with `#[should_panic]`. The failure we
cc61c64b
XL
674got means that the code in the function, `Guess::new(200)`, did not cause a
675panic.
676
677`should_panic` tests can be imprecise, however, because they only tell us that
678the code has caused some panic. A `should_panic` test would pass even if the
679test panics for a different reason than the one we were expecting to happen. To
680make `should_panic` tests more precise, we can add an optional `expected`
681parameter to the `should_panic` attribute. The test harness will make sure that
682the failure message contains the provided text. For example, consider the
683modified code for `Guess` in Listing 11-9 where the `new` function panics with
684different messages depending on whether the value was too small or too large:
685
686<span class="filename">Filename: src/lib.rs</span>
687
688```rust
689struct Guess {
690 value: u32,
691}
692
693impl Guess {
694 pub fn new(value: u32) -> Guess {
695 if value < 1 {
696 panic!("Guess value must be greater than or equal to 1, got {}.",
697 value);
698 } else if value > 100 {
699 panic!("Guess value must be less than or equal to 100, got {}.",
700 value);
701 }
702
703 Guess {
7cac9316 704 value
cc61c64b
XL
705 }
706 }
707}
708
709#[cfg(test)]
710mod tests {
711 use super::*;
712
713 #[test]
714 #[should_panic(expected = "Guess value must be less than or equal to 100")]
715 fn greater_than_100() {
716 Guess::new(200);
717 }
718}
719```
720
721<span class="caption">Listing 11-9: Testing that a condition will cause a
3b2f2976 722`panic!` with a particular panic message</span>
cc61c64b
XL
723
724This test will pass, because the value we put in the `expected` parameter of
725the `should_panic` attribute is a substring of the message that the
726`Guess::new` function panics with. We could have specified the whole panic
727message that we expect, which in this case would be `Guess value must be less
728than or equal to 100, got 200.` It depends on how much of the panic message is
729unique or dynamic and how precise you want your test to be. In this case, a
730substring of the panic message is enough to ensure that the code in the
731function that gets run is the `else if value > 100` case.
732
733To see what happens when a `should_panic` test with an `expected` message
041b39d2 734fails, let’s again introduce a bug into our code by swapping the bodies of the
cc61c64b
XL
735`if value < 1` and the `else if value > 100` blocks:
736
737```rust,ignore
738if value < 1 {
739 panic!("Guess value must be less than or equal to 100, got {}.", value);
740} else if value > 100 {
741 panic!("Guess value must be greater than or equal to 1, got {}.", value);
742}
743```
744
745This time when we run the `should_panic` test, it will fail:
746
747```text
748running 1 test
749test tests::greater_than_100 ... FAILED
750
751failures:
752
753---- tests::greater_than_100 stdout ----
754 thread 'tests::greater_than_100' panicked at 'Guess value must be greater
755 than or equal to 1, got 200.', src/lib.rs:10
756note: Run with `RUST_BACKTRACE=1` for a backtrace.
757note: Panic did not include expected string 'Guess value must be less than or
758equal to 100'
759
760failures:
761 tests::greater_than_100
762
763test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
764```
765
766The failure message indicates that this test did indeed panic as we expected,
767but the panic message `did not include expected string 'Guess value must be
768less than or equal to 100'`. We can see the panic message that we did get,
769which in this case was `Guess value must be greater than or equal to 1, got
770200.` We could then start figuring out where our bug was!
771
041b39d2 772Now that we’ve gone over ways to write tests, let’s look at what is happening
cc61c64b
XL
773when we run our tests and talk about the different options we can use with
774`cargo test`.