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