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