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