]>
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 | |
9fa01778 XL |
285 | we covered in Chapter 7 in the [“Modules as the Privacy |
286 | Boundary”][modules-as-privacy-boundary]<!-- ignore --> section. Because the | |
287 | `tests` module is an inner module, we need to bring the code under test in the | |
288 | outer module into the scope of the inner module. We use a glob here so anything | |
289 | we define in the outer module is available to this `tests` module. | |
13cf67c4 XL |
290 | |
291 | We’ve named our test `larger_can_hold_smaller`, and we’ve created the two | |
292 | `Rectangle` instances that we need. Then we called the `assert!` macro and | |
293 | passed it the result of calling `larger.can_hold(&smaller)`. This expression | |
294 | is supposed to return `true`, so our test should pass. Let’s find out! | |
295 | ||
296 | ```text | |
297 | running 1 test | |
298 | test tests::larger_can_hold_smaller ... ok | |
299 | ||
300 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out | |
301 | ``` | |
302 | ||
303 | It does pass! Let’s add another test, this time asserting that a smaller | |
304 | rectangle cannot hold a larger rectangle: | |
305 | ||
306 | <span class="filename">Filename: src/lib.rs</span> | |
307 | ||
308 | ```rust | |
309 | # fn main() {} | |
310 | #[cfg(test)] | |
311 | mod tests { | |
312 | use super::*; | |
313 | ||
314 | #[test] | |
315 | fn larger_can_hold_smaller() { | |
316 | // --snip-- | |
317 | } | |
318 | ||
319 | #[test] | |
320 | fn smaller_cannot_hold_larger() { | |
9fa01778 XL |
321 | let larger = Rectangle { width: 8, height: 7 }; |
322 | let smaller = Rectangle { width: 5, height: 1 }; | |
13cf67c4 XL |
323 | |
324 | assert!(!smaller.can_hold(&larger)); | |
325 | } | |
326 | } | |
327 | ``` | |
328 | ||
329 | Because the correct result of the `can_hold` function in this case is `false`, | |
330 | we need to negate that result before we pass it to the `assert!` macro. As a | |
331 | result, our test will pass if `can_hold` returns `false`: | |
332 | ||
333 | ```text | |
334 | running 2 tests | |
335 | test tests::smaller_cannot_hold_larger ... ok | |
336 | test tests::larger_can_hold_smaller ... ok | |
337 | ||
338 | test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out | |
339 | ``` | |
340 | ||
341 | Two tests that pass! Now let’s see what happens to our test results when we | |
342 | introduce a bug in our code. Let’s change the implementation of the `can_hold` | |
9fa01778 XL |
343 | method by replacing the greater than sign with a less than sign when it |
344 | compares the widths: | |
13cf67c4 XL |
345 | |
346 | ```rust,not_desired_behavior | |
347 | # fn main() {} | |
348 | # #[derive(Debug)] | |
9fa01778 | 349 | # struct Rectangle { |
13cf67c4 | 350 | # width: u32, |
9fa01778 | 351 | # height: u32, |
13cf67c4 XL |
352 | # } |
353 | // --snip-- | |
354 | ||
355 | impl Rectangle { | |
9fa01778 XL |
356 | fn can_hold(&self, other: &Rectangle) -> bool { |
357 | self.width < other.width && self.height > other.height | |
13cf67c4 XL |
358 | } |
359 | } | |
360 | ``` | |
361 | ||
362 | Running the tests now produces the following: | |
363 | ||
364 | ```text | |
365 | running 2 tests | |
366 | test tests::smaller_cannot_hold_larger ... ok | |
367 | test tests::larger_can_hold_smaller ... FAILED | |
368 | ||
369 | failures: | |
370 | ||
371 | ---- tests::larger_can_hold_smaller stdout ---- | |
9fa01778 XL |
372 | thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed: |
373 | larger.can_hold(&smaller)', src/lib.rs:22:9 | |
13cf67c4 XL |
374 | note: Run with `RUST_BACKTRACE=1` for a backtrace. |
375 | ||
376 | failures: | |
377 | tests::larger_can_hold_smaller | |
378 | ||
379 | test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out | |
380 | ``` | |
381 | ||
9fa01778 XL |
382 | Our tests caught the bug! Because `larger.width` is 8 and `smaller.width` is |
383 | 5, the comparison of the widths in `can_hold` now returns `false`: 8 is not | |
13cf67c4 XL |
384 | less than 5. |
385 | ||
386 | ### Testing Equality with the `assert_eq!` and `assert_ne!` Macros | |
387 | ||
388 | A common way to test functionality is to compare the result of the code under | |
389 | test to the value you expect the code to return to make sure they’re equal. You | |
390 | could do this using the `assert!` macro and passing it an expression using the | |
391 | `==` operator. However, this is such a common test that the standard library | |
392 | provides a pair of macros—`assert_eq!` and `assert_ne!`—to perform this test | |
393 | more conveniently. These macros compare two arguments for equality or | |
394 | inequality, respectively. They’ll also print the two values if the assertion | |
395 | fails, which makes it easier to see *why* the test failed; conversely, the | |
396 | `assert!` macro only indicates that it got a `false` value for the `==` | |
397 | expression, not the values that lead to the `false` value. | |
398 | ||
399 | In Listing 11-7, we write a function named `add_two` that adds `2` to its | |
400 | parameter and returns the result. Then we test this function using the | |
401 | `assert_eq!` macro. | |
402 | ||
403 | <span class="filename">Filename: src/lib.rs</span> | |
404 | ||
405 | ```rust | |
406 | # fn main() {} | |
407 | pub fn add_two(a: i32) -> i32 { | |
408 | a + 2 | |
409 | } | |
410 | ||
411 | #[cfg(test)] | |
412 | mod tests { | |
413 | use super::*; | |
414 | ||
415 | #[test] | |
416 | fn it_adds_two() { | |
417 | assert_eq!(4, add_two(2)); | |
418 | } | |
419 | } | |
420 | ``` | |
421 | ||
422 | <span class="caption">Listing 11-7: Testing the function `add_two` using the | |
423 | `assert_eq!` macro</span> | |
424 | ||
425 | Let’s check that it passes! | |
426 | ||
427 | ```text | |
428 | running 1 test | |
429 | test tests::it_adds_two ... ok | |
430 | ||
431 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out | |
432 | ``` | |
433 | ||
434 | The first argument we gave to the `assert_eq!` macro, `4`, is equal to the | |
435 | result of calling `add_two(2)`. The line for this test is `test | |
436 | tests::it_adds_two ... ok`, and the `ok` text indicates that our test passed! | |
437 | ||
438 | Let’s introduce a bug into our code to see what it looks like when a test that | |
439 | uses `assert_eq!` fails. Change the implementation of the `add_two` function to | |
440 | instead add `3`: | |
441 | ||
442 | ```rust,not_desired_behavior | |
443 | # fn main() {} | |
444 | pub fn add_two(a: i32) -> i32 { | |
445 | a + 3 | |
446 | } | |
447 | ``` | |
448 | ||
449 | Run the tests again: | |
450 | ||
451 | ```text | |
452 | running 1 test | |
453 | test tests::it_adds_two ... FAILED | |
454 | ||
455 | failures: | |
456 | ||
457 | ---- tests::it_adds_two stdout ---- | |
9fa01778 | 458 | thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)` |
13cf67c4 | 459 | left: `4`, |
9fa01778 | 460 | right: `5`', src/lib.rs:11:9 |
13cf67c4 XL |
461 | note: Run with `RUST_BACKTRACE=1` for a backtrace. |
462 | ||
463 | failures: | |
464 | tests::it_adds_two | |
465 | ||
466 | test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out | |
467 | ``` | |
468 | ||
469 | Our test caught the bug! The `it_adds_two` test failed, displaying the message | |
470 | `` assertion failed: `(left == right)` `` and showing that `left` was `4` and | |
471 | `right` was `5`. This message is useful and helps us start debugging: it means | |
472 | the `left` argument to `assert_eq!` was `4` but the `right` argument, where we | |
473 | had `add_two(2)`, was `5`. | |
474 | ||
475 | Note that in some languages and test frameworks, the parameters to the | |
476 | functions that assert two values are equal are called `expected` and `actual`, | |
477 | and the order in which we specify the arguments matters. However, in Rust, | |
478 | they’re called `left` and `right`, and the order in which we specify the value | |
479 | we expect and the value that the code under test produces doesn’t matter. We | |
480 | could write the assertion in this test as `assert_eq!(add_two(2), 4)`, which | |
481 | would result in a failure message that displays `` assertion failed: `(left == | |
482 | right)` `` and that `left` was `5` and `right` was `4`. | |
483 | ||
484 | The `assert_ne!` macro will pass if the two values we give it are not equal and | |
485 | fail if they’re equal. This macro is most useful for cases when we’re not sure | |
486 | what a value *will* be, but we know what the value definitely *won’t* be if our | |
487 | code is functioning as we intend. For example, if we’re testing a function that | |
488 | is guaranteed to change its input in some way, but the way in which the input | |
489 | is changed depends on the day of the week that we run our tests, the best thing | |
490 | to assert might be that the output of the function is not equal to the input. | |
491 | ||
492 | Under the surface, the `assert_eq!` and `assert_ne!` macros use the operators | |
493 | `==` and `!=`, respectively. When the assertions fail, these macros print their | |
494 | arguments using debug formatting, which means the values being compared must | |
495 | implement the `PartialEq` and `Debug` traits. All the primitive types and most | |
496 | of the standard library types implement these traits. For structs and enums | |
497 | that you define, you’ll need to implement `PartialEq` to assert that values of | |
498 | those types are equal or not equal. You’ll need to implement `Debug` to print | |
499 | the values when the assertion fails. Because both traits are derivable traits, | |
500 | as mentioned in Listing 5-12 in Chapter 5, this is usually as straightforward | |
501 | as adding the `#[derive(PartialEq, Debug)]` annotation to your struct or enum | |
9fa01778 XL |
502 | definition. See Appendix C, [“Derivable Traits,”][derivable-traits]<!-- ignore |
503 | --> for more details about these and other derivable traits. | |
13cf67c4 XL |
504 | |
505 | ### Adding Custom Failure Messages | |
506 | ||
507 | You can also add a custom message to be printed with the failure message as | |
508 | optional arguments to the `assert!`, `assert_eq!`, and `assert_ne!` macros. Any | |
509 | arguments specified after the one required argument to `assert!` or the two | |
510 | required arguments to `assert_eq!` and `assert_ne!` are passed along to the | |
9fa01778 | 511 | `format!` macro (discussed in Chapter 8 in the [“Concatenation with the `+` |
dc9dc135 XL |
512 | Operator or the `format!` |
513 | Macro”][concatenation-with-the--operator-or-the-format-macro]<!-- ignore --> | |
514 | section), so you can pass a format string that contains `{}` placeholders and | |
515 | values to go in those placeholders. Custom messages are useful to document | |
516 | what an assertion means; when a test fails, you’ll have a better idea of what | |
517 | the problem is with the code. | |
13cf67c4 XL |
518 | |
519 | For example, let’s say we have a function that greets people by name and we | |
520 | want to test that the name we pass into the function appears in the output: | |
521 | ||
522 | <span class="filename">Filename: src/lib.rs</span> | |
523 | ||
524 | ```rust | |
525 | # fn main() {} | |
526 | pub fn greeting(name: &str) -> String { | |
527 | format!("Hello {}!", name) | |
528 | } | |
529 | ||
530 | #[cfg(test)] | |
531 | mod tests { | |
532 | use super::*; | |
533 | ||
534 | #[test] | |
535 | fn greeting_contains_name() { | |
536 | let result = greeting("Carol"); | |
537 | assert!(result.contains("Carol")); | |
538 | } | |
539 | } | |
540 | ``` | |
541 | ||
542 | The requirements for this program haven’t been agreed upon yet, and we’re | |
543 | pretty sure the `Hello` text at the beginning of the greeting will change. We | |
544 | decided we don’t want to have to update the test when the requirements change, | |
545 | so instead of checking for exact equality to the value returned from the | |
546 | `greeting` function, we’ll just assert that the output contains the text of the | |
547 | input parameter. | |
548 | ||
549 | Let’s introduce a bug into this code by changing `greeting` to not include | |
550 | `name` to see what this test failure looks like: | |
551 | ||
552 | ```rust,not_desired_behavior | |
553 | # fn main() {} | |
554 | pub fn greeting(name: &str) -> String { | |
555 | String::from("Hello!") | |
556 | } | |
557 | ``` | |
558 | ||
559 | Running this test produces the following: | |
560 | ||
561 | ```text | |
562 | running 1 test | |
563 | test tests::greeting_contains_name ... FAILED | |
564 | ||
565 | failures: | |
566 | ||
567 | ---- tests::greeting_contains_name stdout ---- | |
9fa01778 XL |
568 | thread 'tests::greeting_contains_name' panicked at 'assertion failed: |
569 | result.contains("Carol")', src/lib.rs:12:9 | |
13cf67c4 XL |
570 | note: Run with `RUST_BACKTRACE=1` for a backtrace. |
571 | ||
572 | failures: | |
573 | tests::greeting_contains_name | |
574 | ``` | |
575 | ||
576 | This result just indicates that the assertion failed and which line the | |
577 | assertion is on. A more useful failure message in this case would print the | |
578 | value we got from the `greeting` function. Let’s change the test function, | |
579 | giving it a custom failure message made from a format string with a placeholder | |
580 | filled in with the actual value we got from the `greeting` function: | |
581 | ||
582 | ```rust,ignore | |
583 | #[test] | |
584 | fn greeting_contains_name() { | |
585 | let result = greeting("Carol"); | |
586 | assert!( | |
587 | result.contains("Carol"), | |
588 | "Greeting did not contain name, value was `{}`", result | |
589 | ); | |
590 | } | |
591 | ``` | |
592 | ||
593 | Now when we run the test, we’ll get a more informative error message: | |
594 | ||
595 | ```text | |
596 | ---- tests::greeting_contains_name stdout ---- | |
9fa01778 XL |
597 | thread 'tests::greeting_contains_name' panicked at 'Greeting did not |
598 | contain name, value was `Hello!`', src/lib.rs:12:9 | |
13cf67c4 XL |
599 | note: Run with `RUST_BACKTRACE=1` for a backtrace. |
600 | ``` | |
601 | ||
602 | We can see the value we actually got in the test output, which would help us | |
603 | debug what happened instead of what we were expecting to happen. | |
604 | ||
605 | ### Checking for Panics with `should_panic` | |
606 | ||
607 | In addition to checking that our code returns the correct values we expect, | |
608 | it’s also important to check that our code handles error conditions as we | |
609 | expect. For example, consider the `Guess` type that we created in Chapter 9, | |
610 | Listing 9-10. Other code that uses `Guess` depends on the guarantee that `Guess` | |
611 | instances will contain only values between 1 and 100. We can write a test that | |
612 | ensures that attempting to create a `Guess` instance with a value outside that | |
613 | range panics. | |
614 | ||
615 | We do this by adding another attribute, `should_panic`, to our test function. | |
616 | This attribute makes a test pass if the code inside the function panics; the | |
617 | test will fail if the code inside the function doesn’t panic. | |
618 | ||
619 | Listing 11-8 shows a test that checks that the error conditions of `Guess::new` | |
9fa01778 | 620 | happen when we expect them to. |
13cf67c4 XL |
621 | |
622 | <span class="filename">Filename: src/lib.rs</span> | |
623 | ||
624 | ```rust | |
625 | # fn main() {} | |
626 | pub struct Guess { | |
627 | value: i32, | |
628 | } | |
629 | ||
630 | impl Guess { | |
631 | pub fn new(value: i32) -> Guess { | |
632 | if value < 1 || value > 100 { | |
633 | panic!("Guess value must be between 1 and 100, got {}.", value); | |
634 | } | |
635 | ||
636 | Guess { | |
637 | value | |
638 | } | |
639 | } | |
640 | } | |
641 | ||
642 | #[cfg(test)] | |
643 | mod tests { | |
644 | use super::*; | |
645 | ||
646 | #[test] | |
647 | #[should_panic] | |
648 | fn greater_than_100() { | |
649 | Guess::new(200); | |
650 | } | |
651 | } | |
652 | ``` | |
653 | ||
654 | <span class="caption">Listing 11-8: Testing that a condition will cause a | |
655 | `panic!`</span> | |
656 | ||
657 | We place the `#[should_panic]` attribute after the `#[test]` attribute and | |
658 | before the test function it applies to. Let’s look at the result when this test | |
659 | passes: | |
660 | ||
661 | ```text | |
662 | running 1 test | |
663 | test tests::greater_than_100 ... ok | |
664 | ||
665 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out | |
666 | ``` | |
667 | ||
668 | Looks good! Now let’s introduce a bug in our code by removing the condition | |
669 | that the `new` function will panic if the value is greater than 100: | |
670 | ||
671 | ```rust,not_desired_behavior | |
672 | # fn main() {} | |
673 | # pub struct Guess { | |
674 | # value: i32, | |
675 | # } | |
676 | # | |
677 | // --snip-- | |
678 | ||
679 | impl Guess { | |
680 | pub fn new(value: i32) -> Guess { | |
681 | if value < 1 { | |
682 | panic!("Guess value must be between 1 and 100, got {}.", value); | |
683 | } | |
684 | ||
685 | Guess { | |
686 | value | |
687 | } | |
688 | } | |
689 | } | |
690 | ``` | |
691 | ||
692 | When we run the test in Listing 11-8, it will fail: | |
693 | ||
694 | ```text | |
695 | running 1 test | |
696 | test tests::greater_than_100 ... FAILED | |
697 | ||
698 | failures: | |
699 | ||
700 | failures: | |
701 | tests::greater_than_100 | |
702 | ||
703 | test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out | |
704 | ``` | |
705 | ||
706 | We don’t get a very helpful message in this case, but when we look at the test | |
707 | function, we see that it’s annotated with `#[should_panic]`. The failure we got | |
708 | means that the code in the test function did not cause a panic. | |
709 | ||
710 | Tests that use `should_panic` can be imprecise because they only indicate that | |
711 | the code has caused some panic. A `should_panic` test would pass even if the | |
532ac7d7 | 712 | test panics for a different reason from the one we were expecting to happen. To |
13cf67c4 XL |
713 | make `should_panic` tests more precise, we can add an optional `expected` |
714 | parameter to the `should_panic` attribute. The test harness will make sure that | |
715 | the failure message contains the provided text. For example, consider the | |
716 | modified code for `Guess` in Listing 11-9 where the `new` function panics with | |
9fa01778 | 717 | different messages depending on whether the value is too small or too large. |
13cf67c4 XL |
718 | |
719 | <span class="filename">Filename: src/lib.rs</span> | |
720 | ||
721 | ```rust | |
722 | # fn main() {} | |
723 | # pub struct Guess { | |
724 | # value: i32, | |
725 | # } | |
726 | # | |
727 | // --snip-- | |
728 | ||
729 | impl Guess { | |
730 | pub fn new(value: i32) -> Guess { | |
731 | if value < 1 { | |
732 | panic!("Guess value must be greater than or equal to 1, got {}.", | |
733 | value); | |
734 | } else if value > 100 { | |
735 | panic!("Guess value must be less than or equal to 100, got {}.", | |
736 | value); | |
737 | } | |
738 | ||
739 | Guess { | |
740 | value | |
741 | } | |
742 | } | |
743 | } | |
744 | ||
745 | #[cfg(test)] | |
746 | mod tests { | |
747 | use super::*; | |
748 | ||
749 | #[test] | |
750 | #[should_panic(expected = "Guess value must be less than or equal to 100")] | |
751 | fn greater_than_100() { | |
752 | Guess::new(200); | |
753 | } | |
754 | } | |
755 | ``` | |
756 | ||
757 | <span class="caption">Listing 11-9: Testing that a condition will cause a | |
758 | `panic!` with a particular panic message</span> | |
759 | ||
760 | This test will pass because the value we put in the `should_panic` attribute’s | |
761 | `expected` parameter is a substring of the message that the `Guess::new` | |
762 | function panics with. We could have specified the entire panic message that we | |
763 | expect, which in this case would be `Guess value must be less than or equal to | |
764 | 100, got 200.` What you choose to specify in the expected parameter for | |
765 | `should_panic` depends on how much of the panic message is unique or dynamic | |
766 | and how precise you want your test to be. In this case, a substring of the | |
767 | panic message is enough to ensure that the code in the test function executes | |
768 | the `else if value > 100` case. | |
769 | ||
770 | To see what happens when a `should_panic` test with an `expected` message | |
771 | fails, let’s again introduce a bug into our code by swapping the bodies of the | |
772 | `if value < 1` and the `else if value > 100` blocks: | |
773 | ||
774 | ```rust,ignore,not_desired_behavior | |
775 | if value < 1 { | |
776 | panic!("Guess value must be less than or equal to 100, got {}.", value); | |
777 | } else if value > 100 { | |
778 | panic!("Guess value must be greater than or equal to 1, got {}.", value); | |
779 | } | |
780 | ``` | |
781 | ||
782 | This time when we run the `should_panic` test, it will fail: | |
783 | ||
784 | ```text | |
785 | running 1 test | |
786 | test tests::greater_than_100 ... FAILED | |
787 | ||
788 | failures: | |
789 | ||
790 | ---- tests::greater_than_100 stdout ---- | |
9fa01778 XL |
791 | thread 'tests::greater_than_100' panicked at 'Guess value must be |
792 | greater than or equal to 1, got 200.', src/lib.rs:11:13 | |
13cf67c4 XL |
793 | note: Run with `RUST_BACKTRACE=1` for a backtrace. |
794 | note: Panic did not include expected string 'Guess value must be less than or | |
795 | equal to 100' | |
796 | ||
797 | failures: | |
798 | tests::greater_than_100 | |
799 | ||
800 | test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out | |
801 | ``` | |
802 | ||
803 | The failure message indicates that this test did indeed panic as we expected, | |
804 | but the panic message did not include the expected string `'Guess value must be | |
805 | less than or equal to 100'`. The panic message that we did get in this case was | |
806 | `Guess value must be greater than or equal to 1, got 200.` Now we can start | |
807 | figuring out where our bug is! | |
808 | ||
9fa01778 | 809 | ### Using `Result<T, E>` in Tests |
13cf67c4 XL |
810 | |
811 | So far, we’ve written tests that panic when they fail. We can also write tests | |
9fa01778 | 812 | that use `Result<T, E>`! Here’s the test from Listing 11-1, rewritten to use |
532ac7d7 | 813 | `Result<T, E>` and return an `Err` instead of panicking: |
13cf67c4 XL |
814 | |
815 | ```rust | |
816 | #[cfg(test)] | |
817 | mod tests { | |
818 | #[test] | |
819 | fn it_works() -> Result<(), String> { | |
820 | if 2 + 2 == 4 { | |
821 | Ok(()) | |
822 | } else { | |
823 | Err(String::from("two plus two does not equal four")) | |
824 | } | |
825 | } | |
826 | } | |
827 | ``` | |
828 | ||
9fa01778 | 829 | The `it_works` function now has a return type, `Result<(), String>`. In the |
532ac7d7 | 830 | body of the function, rather than calling the `assert_eq!` macro, we return |
9fa01778 | 831 | `Ok(())` when the test passes and an `Err` with a `String` inside when the test |
532ac7d7 | 832 | fails. |
9fa01778 | 833 | |
532ac7d7 XL |
834 | Writing tests so they return a `Result<T, E>` enables you to use the question |
835 | mark operator in the body of tests, which can be a convenient way to write | |
836 | tests that should fail if any operation within them returns an `Err` variant. | |
837 | ||
838 | You can’t use the `#[should_panic]` annotation on tests that use `Result<T, | |
9fa01778 XL |
839 | E>`. Instead, you should return an `Err` value directly when the test should |
840 | fail. | |
13cf67c4 XL |
841 | |
842 | Now that you know several ways to write tests, let’s look at what is happening | |
843 | when we run our tests and explore the different options we can use with `cargo | |
844 | test`. | |
9fa01778 XL |
845 | |
846 | [concatenation-with-the--operator-or-the-format-macro]: | |
847 | ch08-02-strings.html#concatenation-with-the--operator-or-the-format-macro | |
848 | [controlling-how-tests-are-run]: | |
849 | ch11-02-running-tests.html#controlling-how-tests-are-run | |
850 | [derivable-traits]: appendix-03-derivable-traits.html | |
851 | [doc-comments]: ch14-02-publishing-to-crates-io.html#documentation-comments-as-tests | |
532ac7d7 | 852 | [modules-as-privacy-boundary]: ch07-02-defining-modules-to-control-scope-and-privacy.html |