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