]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | ## How to Write Tests |
2 | ||
3 | Tests are Rust functions that verify that the non-test code is functioning in | |
4 | the expected manner. The bodies of test functions typically perform these three | |
5 | actions: | |
6 | ||
7 | 1. Set up any needed data or state. | |
8 | 2. Run the code you want to test. | |
9 | 3. Assert the results are what you expect. | |
10 | ||
11 | Let’s look at the features Rust provides specifically for writing tests that | |
12 | take these actions, which include the `test` attribute, a few macros, and the | |
13 | `should_panic` attribute. | |
14 | ||
15 | ### The Anatomy of a Test Function | |
16 | ||
17 | At its simplest, a test in Rust is a function that’s annotated with the `test` | |
18 | attribute. Attributes are metadata about pieces of Rust code; one example is | |
19 | the `derive` attribute we used with structs in Chapter 5. To change a function | |
20 | into a test function, add `#[test]` on the line before `fn`. When you run your | |
21 | tests with the `cargo test` command, Rust builds a test runner binary that runs | |
22 | the functions annotated with the `test` attribute and reports on whether each | |
23 | test function passes or fails. | |
24 | ||
25 | When we make a new library project with Cargo, a test module with a test | |
26 | function in it is automatically generated for us. This module helps you start | |
27 | writing your tests so you don’t have to look up the exact structure and syntax | |
28 | of test functions every time you start a new project. You can add as many | |
29 | additional test functions and as many test modules as you want! | |
30 | ||
31 | We’ll explore some aspects of how tests work by experimenting with the template | |
32 | test generated for us without actually testing any code. Then we’ll write some | |
33 | real-world tests that call some code that we’ve written and assert that its | |
34 | behavior is correct. | |
35 | ||
36 | Let’s create a new library project called `adder`: | |
37 | ||
f035d41b | 38 | ```console |
13cf67c4 XL |
39 | $ cargo new adder --lib |
40 | Created library `adder` project | |
41 | $ cd adder | |
42 | ``` | |
43 | ||
44 | The contents of the *src/lib.rs* file in your `adder` library should look like | |
9fa01778 | 45 | Listing 11-1. |
13cf67c4 XL |
46 | |
47 | <span class="filename">Filename: src/lib.rs</span> | |
48 | ||
ee023bcb FG |
49 | <!-- manual-regeneration |
50 | cd listings/ch11-writing-automated-tests | |
51 | rm -rf listing-11-01 | |
52 | cargo new --lib listing-11-01 --name adder | |
53 | cd listing-11-01 | |
54 | cargo test | |
55 | git co output.txt | |
56 | cd ../../.. | |
57 | --> | |
58 | ||
fc512014 XL |
59 | ```rust,noplayground |
60 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-01/src/lib.rs}} | |
13cf67c4 XL |
61 | ``` |
62 | ||
63 | <span class="caption">Listing 11-1: The test module and function generated | |
64 | automatically 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 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 by using the `#[test]` attribute. | |
72 | ||
73 | The function body uses the `assert_eq!` macro to assert that 2 + 2 equals 4. | |
74 | This assertion serves as an example of the format for a typical test. Let’s run | |
75 | it to see that this test passes. | |
76 | ||
77 | The `cargo test` command runs all tests in our project, as shown in Listing | |
9fa01778 | 78 | 11-2. |
13cf67c4 | 79 | |
f035d41b | 80 | ```console |
74b04a01 | 81 | {{#include ../listings/ch11-writing-automated-tests/listing-11-01/output.txt}} |
13cf67c4 XL |
82 | ``` |
83 | ||
84 | <span class="caption">Listing 11-2: The output from running the automatically | |
85 | generated test</span> | |
86 | ||
87 | Cargo compiled and ran the test. After the `Compiling`, `Finished`, and | |
88 | `Running` lines is the line `running 1 test`. The next line shows the name | |
89 | of the generated test function, called `it_works`, and the result of running | |
90 | that test, `ok`. The overall summary of running the tests appears next. The | |
91 | text `test result: ok.` means that all the tests passed, and the portion that | |
92 | reads `1 passed; 0 failed` totals the number of tests that passed or failed. | |
93 | ||
94 | Because we don’t have any tests we’ve marked as ignored, the summary shows `0 | |
95 | ignored`. We also haven’t filtered the tests being run, so the end of the | |
96 | summary shows `0 filtered out`. We’ll talk about ignoring and filtering out | |
dc9dc135 XL |
97 | tests in the next section, [“Controlling How Tests Are |
98 | Run.”][controlling-how-tests-are-run]<!-- ignore --> | |
13cf67c4 XL |
99 | |
100 | The `0 measured` statistic is for benchmark tests that measure performance. | |
101 | Benchmark tests are, as of this writing, only available in nightly Rust. See | |
102 | [the documentation about benchmark tests][bench] to learn more. | |
103 | ||
104 | [bench]: ../unstable-book/library-features/test.html | |
105 | ||
106 | The next part of the test output, which starts with `Doc-tests adder`, is for | |
107 | the results of any documentation tests. We don’t have any documentation tests | |
108 | yet, but Rust can compile any code examples that appear in our API | |
109 | documentation. This feature helps us keep our docs and our code in sync! We’ll | |
9fa01778 XL |
110 | discuss how to write documentation tests in the [“Documentation Comments as |
111 | Tests”][doc-comments]<!-- ignore --> section of Chapter 14. For now, we’ll | |
112 | ignore the `Doc-tests` output. | |
13cf67c4 XL |
113 | |
114 | Let’s change the name of our test to see how that changes the test output. | |
115 | Change the `it_works` function to a different name, such as `exploration`, like | |
116 | so: | |
117 | ||
118 | <span class="filename">Filename: src/lib.rs</span> | |
119 | ||
fc512014 XL |
120 | ```rust,noplayground |
121 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-01-changing-test-name/src/lib.rs}} | |
13cf67c4 XL |
122 | ``` |
123 | ||
124 | Then run `cargo test` again. The output now shows `exploration` instead of | |
125 | `it_works`: | |
126 | ||
f035d41b | 127 | ```console |
74b04a01 | 128 | {{#include ../listings/ch11-writing-automated-tests/no-listing-01-changing-test-name/output.txt}} |
13cf67c4 XL |
129 | ``` |
130 | ||
131 | Let’s add another test, but this time we’ll make a test that fails! Tests fail | |
132 | when something in the test function panics. Each test is run in a new thread, | |
133 | and when the main thread sees that a test thread has died, the test is marked | |
134 | as failed. We talked about the simplest way to cause a panic in Chapter 9, | |
135 | which is to call the `panic!` macro. Enter the new test, `another`, so your | |
9fa01778 | 136 | *src/lib.rs* file looks like Listing 11-3. |
13cf67c4 XL |
137 | |
138 | <span class="filename">Filename: src/lib.rs</span> | |
139 | ||
fc512014 | 140 | ```rust,panics,noplayground |
74b04a01 | 141 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-03/src/lib.rs:here}} |
13cf67c4 XL |
142 | ``` |
143 | ||
144 | <span class="caption">Listing 11-3: Adding a second test that will fail because | |
145 | we call the `panic!` macro</span> | |
146 | ||
147 | Run the tests again using `cargo test`. The output should look like Listing | |
9fa01778 | 148 | 11-4, which shows that our `exploration` test passed and `another` failed. |
13cf67c4 | 149 | |
6a06907d | 150 | ```console |
74b04a01 | 151 | {{#include ../listings/ch11-writing-automated-tests/listing-11-03/output.txt}} |
13cf67c4 XL |
152 | ``` |
153 | ||
154 | <span class="caption">Listing 11-4: Test results when one test passes and one | |
155 | test fails</span> | |
156 | ||
157 | Instead of `ok`, the line `test tests::another` shows `FAILED`. Two new | |
158 | sections appear between the individual results and the summary: the first | |
159 | section displays the detailed reason for each test failure. In this case, | |
160 | `another` failed because it `panicked at 'Make this test fail'`, which happened | |
161 | on line 10 in the *src/lib.rs* file. The next section lists just the names of | |
162 | all the failing tests, which is useful when there are lots of tests and lots of | |
163 | detailed failing test output. We can use the name of a failing test to run just | |
164 | that test to more easily debug it; we’ll talk more about ways to run tests in | |
9fa01778 XL |
165 | the [“Controlling How Tests Are Run”][controlling-how-tests-are-run]<!-- ignore |
166 | --> section. | |
13cf67c4 XL |
167 | |
168 | The summary line displays at the end: overall, our test result is `FAILED`. | |
169 | We had one test pass and one test fail. | |
170 | ||
171 | Now that you’ve seen what the test results look like in different scenarios, | |
172 | let’s look at some macros other than `panic!` that are useful in tests. | |
173 | ||
174 | ### Checking Results with the `assert!` Macro | |
175 | ||
176 | The `assert!` macro, provided by the standard library, is useful when you want | |
177 | to ensure that some condition in a test evaluates to `true`. We give the | |
178 | `assert!` macro an argument that evaluates to a Boolean. If the value is | |
179 | `true`, `assert!` does nothing and the test passes. If the value is `false`, | |
180 | the `assert!` macro calls the `panic!` macro, which causes the test to fail. | |
181 | Using the `assert!` macro helps us check that our code is functioning in the | |
182 | way we intend. | |
183 | ||
184 | In Chapter 5, Listing 5-15, we used a `Rectangle` struct and a `can_hold` | |
185 | method, which are repeated here in Listing 11-5. Let’s put this code in the | |
186 | *src/lib.rs* file and write some tests for it using the `assert!` macro. | |
187 | ||
188 | <span class="filename">Filename: src/lib.rs</span> | |
189 | ||
fc512014 | 190 | ```rust,noplayground |
74b04a01 | 191 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-05/src/lib.rs:here}} |
13cf67c4 XL |
192 | ``` |
193 | ||
194 | <span class="caption">Listing 11-5: Using the `Rectangle` struct and its | |
195 | `can_hold` method from Chapter 5</span> | |
196 | ||
197 | The `can_hold` method returns a Boolean, which means it’s a perfect use case | |
198 | for the `assert!` macro. In Listing 11-6, we write a test that exercises the | |
9fa01778 XL |
199 | `can_hold` method by creating a `Rectangle` instance that has a width of 8 and |
200 | a height of 7 and asserting that it can hold another `Rectangle` instance that | |
201 | has a width of 5 and a height of 1. | |
13cf67c4 XL |
202 | |
203 | <span class="filename">Filename: src/lib.rs</span> | |
204 | ||
fc512014 | 205 | ```rust,noplayground |
74b04a01 | 206 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-06/src/lib.rs:here}} |
13cf67c4 XL |
207 | ``` |
208 | ||
209 | <span class="caption">Listing 11-6: A test for `can_hold` that checks whether a | |
210 | larger rectangle can indeed hold a smaller rectangle</span> | |
211 | ||
212 | Note that we’ve added a new line inside the `tests` module: `use super::*;`. | |
213 | The `tests` module is a regular module that follows the usual visibility rules | |
416331ca XL |
214 | we covered in Chapter 7 in the [“Paths for Referring to an Item in the Module |
215 | Tree”][paths-for-referring-to-an-item-in-the-module-tree]<!-- ignore --> | |
216 | section. Because the `tests` module is an inner module, we need to bring the | |
217 | code under test in the outer module into the scope of the inner module. We use | |
218 | a glob here so anything we define in the outer module is available to this | |
219 | `tests` module. | |
13cf67c4 XL |
220 | |
221 | We’ve named our test `larger_can_hold_smaller`, and we’ve created the two | |
222 | `Rectangle` instances that we need. Then we called the `assert!` macro and | |
223 | passed it the result of calling `larger.can_hold(&smaller)`. This expression | |
224 | is supposed to return `true`, so our test should pass. Let’s find out! | |
225 | ||
f035d41b | 226 | ```console |
74b04a01 | 227 | {{#include ../listings/ch11-writing-automated-tests/listing-11-06/output.txt}} |
13cf67c4 XL |
228 | ``` |
229 | ||
230 | It does pass! Let’s add another test, this time asserting that a smaller | |
231 | rectangle cannot hold a larger rectangle: | |
232 | ||
233 | <span class="filename">Filename: src/lib.rs</span> | |
234 | ||
fc512014 | 235 | ```rust,noplayground |
74b04a01 | 236 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-02-adding-another-rectangle-test/src/lib.rs:here}} |
13cf67c4 XL |
237 | ``` |
238 | ||
239 | Because the correct result of the `can_hold` function in this case is `false`, | |
240 | we need to negate that result before we pass it to the `assert!` macro. As a | |
241 | result, our test will pass if `can_hold` returns `false`: | |
242 | ||
f035d41b | 243 | ```console |
74b04a01 | 244 | {{#include ../listings/ch11-writing-automated-tests/no-listing-02-adding-another-rectangle-test/output.txt}} |
13cf67c4 XL |
245 | ``` |
246 | ||
247 | Two tests that pass! Now let’s see what happens to our test results when we | |
248 | introduce a bug in our code. Let’s change the implementation of the `can_hold` | |
9fa01778 XL |
249 | method by replacing the greater than sign with a less than sign when it |
250 | compares the widths: | |
13cf67c4 | 251 | |
fc512014 | 252 | ```rust,not_desired_behavior,noplayground |
74b04a01 | 253 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/src/lib.rs:here}} |
13cf67c4 XL |
254 | ``` |
255 | ||
256 | Running the tests now produces the following: | |
257 | ||
f035d41b | 258 | ```console |
74b04a01 | 259 | {{#include ../listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/output.txt}} |
13cf67c4 XL |
260 | ``` |
261 | ||
9fa01778 XL |
262 | Our tests caught the bug! Because `larger.width` is 8 and `smaller.width` is |
263 | 5, the comparison of the widths in `can_hold` now returns `false`: 8 is not | |
13cf67c4 XL |
264 | less than 5. |
265 | ||
266 | ### Testing Equality with the `assert_eq!` and `assert_ne!` Macros | |
267 | ||
268 | A common way to test functionality is to compare the result of the code under | |
269 | test to the value you expect the code to return to make sure they’re equal. You | |
270 | could do this using the `assert!` macro and passing it an expression using the | |
271 | `==` operator. However, this is such a common test that the standard library | |
272 | provides a pair of macros—`assert_eq!` and `assert_ne!`—to perform this test | |
273 | more conveniently. These macros compare two arguments for equality or | |
274 | inequality, respectively. They’ll also print the two values if the assertion | |
275 | fails, which makes it easier to see *why* the test failed; conversely, the | |
276 | `assert!` macro only indicates that it got a `false` value for the `==` | |
17df50a5 | 277 | expression, not the values that led to the `false` value. |
13cf67c4 XL |
278 | |
279 | In Listing 11-7, we write a function named `add_two` that adds `2` to its | |
280 | parameter and returns the result. Then we test this function using the | |
281 | `assert_eq!` macro. | |
282 | ||
283 | <span class="filename">Filename: src/lib.rs</span> | |
284 | ||
fc512014 XL |
285 | ```rust,noplayground |
286 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-07/src/lib.rs}} | |
13cf67c4 XL |
287 | ``` |
288 | ||
289 | <span class="caption">Listing 11-7: Testing the function `add_two` using the | |
290 | `assert_eq!` macro</span> | |
291 | ||
292 | Let’s check that it passes! | |
293 | ||
f035d41b | 294 | ```console |
74b04a01 | 295 | {{#include ../listings/ch11-writing-automated-tests/listing-11-07/output.txt}} |
13cf67c4 XL |
296 | ``` |
297 | ||
298 | The first argument we gave to the `assert_eq!` macro, `4`, is equal to the | |
299 | result of calling `add_two(2)`. The line for this test is `test | |
300 | tests::it_adds_two ... ok`, and the `ok` text indicates that our test passed! | |
301 | ||
302 | Let’s introduce a bug into our code to see what it looks like when a test that | |
303 | uses `assert_eq!` fails. Change the implementation of the `add_two` function to | |
304 | instead add `3`: | |
305 | ||
fc512014 | 306 | ```rust,not_desired_behavior,noplayground |
74b04a01 | 307 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/src/lib.rs:here}} |
13cf67c4 XL |
308 | ``` |
309 | ||
310 | Run the tests again: | |
311 | ||
f035d41b | 312 | ```console |
74b04a01 | 313 | {{#include ../listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/output.txt}} |
13cf67c4 XL |
314 | ``` |
315 | ||
316 | Our test caught the bug! The `it_adds_two` test failed, displaying the message | |
317 | `` assertion failed: `(left == right)` `` and showing that `left` was `4` and | |
318 | `right` was `5`. This message is useful and helps us start debugging: it means | |
319 | the `left` argument to `assert_eq!` was `4` but the `right` argument, where we | |
320 | had `add_two(2)`, was `5`. | |
321 | ||
322 | Note that in some languages and test frameworks, the parameters to the | |
323 | functions that assert two values are equal are called `expected` and `actual`, | |
324 | and the order in which we specify the arguments matters. However, in Rust, | |
325 | they’re called `left` and `right`, and the order in which we specify the value | |
326 | we expect and the value that the code under test produces doesn’t matter. We | |
327 | could write the assertion in this test as `assert_eq!(add_two(2), 4)`, which | |
328 | would result in a failure message that displays `` assertion failed: `(left == | |
329 | right)` `` and that `left` was `5` and `right` was `4`. | |
330 | ||
331 | The `assert_ne!` macro will pass if the two values we give it are not equal and | |
332 | fail if they’re equal. This macro is most useful for cases when we’re not sure | |
333 | what a value *will* be, but we know what the value definitely *won’t* be if our | |
334 | code is functioning as we intend. For example, if we’re testing a function that | |
335 | is guaranteed to change its input in some way, but the way in which the input | |
336 | is changed depends on the day of the week that we run our tests, the best thing | |
337 | to assert might be that the output of the function is not equal to the input. | |
338 | ||
339 | Under the surface, the `assert_eq!` and `assert_ne!` macros use the operators | |
340 | `==` and `!=`, respectively. When the assertions fail, these macros print their | |
341 | arguments using debug formatting, which means the values being compared must | |
342 | implement the `PartialEq` and `Debug` traits. All the primitive types and most | |
343 | of the standard library types implement these traits. For structs and enums | |
344 | that you define, you’ll need to implement `PartialEq` to assert that values of | |
345 | those types are equal or not equal. You’ll need to implement `Debug` to print | |
346 | the values when the assertion fails. Because both traits are derivable traits, | |
347 | as mentioned in Listing 5-12 in Chapter 5, this is usually as straightforward | |
348 | as adding the `#[derive(PartialEq, Debug)]` annotation to your struct or enum | |
9fa01778 XL |
349 | definition. See Appendix C, [“Derivable Traits,”][derivable-traits]<!-- ignore |
350 | --> for more details about these and other derivable traits. | |
13cf67c4 XL |
351 | |
352 | ### Adding Custom Failure Messages | |
353 | ||
354 | You can also add a custom message to be printed with the failure message as | |
355 | optional arguments to the `assert!`, `assert_eq!`, and `assert_ne!` macros. Any | |
356 | arguments specified after the one required argument to `assert!` or the two | |
357 | required arguments to `assert_eq!` and `assert_ne!` are passed along to the | |
9fa01778 | 358 | `format!` macro (discussed in Chapter 8 in the [“Concatenation with the `+` |
dc9dc135 XL |
359 | Operator or the `format!` |
360 | Macro”][concatenation-with-the--operator-or-the-format-macro]<!-- ignore --> | |
361 | section), so you can pass a format string that contains `{}` placeholders and | |
362 | values to go in those placeholders. Custom messages are useful to document | |
363 | what an assertion means; when a test fails, you’ll have a better idea of what | |
364 | the problem is with the code. | |
13cf67c4 XL |
365 | |
366 | For example, let’s say we have a function that greets people by name and we | |
367 | want to test that the name we pass into the function appears in the output: | |
368 | ||
369 | <span class="filename">Filename: src/lib.rs</span> | |
370 | ||
fc512014 XL |
371 | ```rust,noplayground |
372 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-05-greeter/src/lib.rs}} | |
13cf67c4 XL |
373 | ``` |
374 | ||
375 | The requirements for this program haven’t been agreed upon yet, and we’re | |
376 | pretty sure the `Hello` text at the beginning of the greeting will change. We | |
377 | decided we don’t want to have to update the test when the requirements change, | |
378 | so instead of checking for exact equality to the value returned from the | |
379 | `greeting` function, we’ll just assert that the output contains the text of the | |
380 | input parameter. | |
381 | ||
382 | Let’s introduce a bug into this code by changing `greeting` to not include | |
383 | `name` to see what this test failure looks like: | |
384 | ||
fc512014 | 385 | ```rust,not_desired_behavior,noplayground |
74b04a01 | 386 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-06-greeter-with-bug/src/lib.rs:here}} |
13cf67c4 XL |
387 | ``` |
388 | ||
389 | Running this test produces the following: | |
390 | ||
f035d41b | 391 | ```console |
74b04a01 | 392 | {{#include ../listings/ch11-writing-automated-tests/no-listing-06-greeter-with-bug/output.txt}} |
13cf67c4 XL |
393 | ``` |
394 | ||
395 | This result just indicates that the assertion failed and which line the | |
396 | assertion is on. A more useful failure message in this case would print the | |
397 | value we got from the `greeting` function. Let’s change the test function, | |
398 | giving it a custom failure message made from a format string with a placeholder | |
399 | filled in with the actual value we got from the `greeting` function: | |
400 | ||
401 | ```rust,ignore | |
74b04a01 | 402 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-07-custom-failure-message/src/lib.rs:here}} |
13cf67c4 XL |
403 | ``` |
404 | ||
405 | Now when we run the test, we’ll get a more informative error message: | |
406 | ||
f035d41b | 407 | ```console |
74b04a01 | 408 | {{#include ../listings/ch11-writing-automated-tests/no-listing-07-custom-failure-message/output.txt}} |
13cf67c4 XL |
409 | ``` |
410 | ||
411 | We can see the value we actually got in the test output, which would help us | |
412 | debug what happened instead of what we were expecting to happen. | |
413 | ||
414 | ### Checking for Panics with `should_panic` | |
415 | ||
416 | In addition to checking that our code returns the correct values we expect, | |
417 | it’s also important to check that our code handles error conditions as we | |
418 | expect. For example, consider the `Guess` type that we created in Chapter 9, | |
3c0e092e | 419 | Listing 9-13. Other code that uses `Guess` depends on the guarantee that `Guess` |
13cf67c4 XL |
420 | instances will contain only values between 1 and 100. We can write a test that |
421 | ensures that attempting to create a `Guess` instance with a value outside that | |
422 | range panics. | |
423 | ||
424 | We do this by adding another attribute, `should_panic`, to our test function. | |
425 | This attribute makes a test pass if the code inside the function panics; the | |
426 | test will fail if the code inside the function doesn’t panic. | |
427 | ||
428 | Listing 11-8 shows a test that checks that the error conditions of `Guess::new` | |
9fa01778 | 429 | happen when we expect them to. |
13cf67c4 XL |
430 | |
431 | <span class="filename">Filename: src/lib.rs</span> | |
432 | ||
fc512014 XL |
433 | ```rust,noplayground |
434 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-08/src/lib.rs}} | |
13cf67c4 XL |
435 | ``` |
436 | ||
437 | <span class="caption">Listing 11-8: Testing that a condition will cause a | |
438 | `panic!`</span> | |
439 | ||
440 | We place the `#[should_panic]` attribute after the `#[test]` attribute and | |
441 | before the test function it applies to. Let’s look at the result when this test | |
442 | passes: | |
443 | ||
f035d41b | 444 | ```console |
74b04a01 | 445 | {{#include ../listings/ch11-writing-automated-tests/listing-11-08/output.txt}} |
13cf67c4 XL |
446 | ``` |
447 | ||
448 | Looks good! Now let’s introduce a bug in our code by removing the condition | |
449 | that the `new` function will panic if the value is greater than 100: | |
450 | ||
fc512014 | 451 | ```rust,not_desired_behavior,noplayground |
74b04a01 | 452 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-08-guess-with-bug/src/lib.rs:here}} |
13cf67c4 XL |
453 | ``` |
454 | ||
455 | When we run the test in Listing 11-8, it will fail: | |
456 | ||
f035d41b | 457 | ```console |
74b04a01 | 458 | {{#include ../listings/ch11-writing-automated-tests/no-listing-08-guess-with-bug/output.txt}} |
13cf67c4 XL |
459 | ``` |
460 | ||
461 | We don’t get a very helpful message in this case, but when we look at the test | |
462 | function, we see that it’s annotated with `#[should_panic]`. The failure we got | |
463 | means that the code in the test function did not cause a panic. | |
464 | ||
465 | Tests that use `should_panic` can be imprecise because they only indicate that | |
466 | the code has caused some panic. A `should_panic` test would pass even if the | |
532ac7d7 | 467 | test panics for a different reason from the one we were expecting to happen. To |
13cf67c4 XL |
468 | make `should_panic` tests more precise, we can add an optional `expected` |
469 | parameter to the `should_panic` attribute. The test harness will make sure that | |
470 | the failure message contains the provided text. For example, consider the | |
471 | modified code for `Guess` in Listing 11-9 where the `new` function panics with | |
9fa01778 | 472 | different messages depending on whether the value is too small or too large. |
13cf67c4 XL |
473 | |
474 | <span class="filename">Filename: src/lib.rs</span> | |
475 | ||
fc512014 | 476 | ```rust,noplayground |
74b04a01 | 477 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-09/src/lib.rs:here}} |
13cf67c4 XL |
478 | ``` |
479 | ||
480 | <span class="caption">Listing 11-9: Testing that a condition will cause a | |
481 | `panic!` with a particular panic message</span> | |
482 | ||
483 | This test will pass because the value we put in the `should_panic` attribute’s | |
484 | `expected` parameter is a substring of the message that the `Guess::new` | |
485 | function panics with. We could have specified the entire panic message that we | |
486 | expect, which in this case would be `Guess value must be less than or equal to | |
487 | 100, got 200.` What you choose to specify in the expected parameter for | |
488 | `should_panic` depends on how much of the panic message is unique or dynamic | |
489 | and how precise you want your test to be. In this case, a substring of the | |
490 | panic message is enough to ensure that the code in the test function executes | |
491 | the `else if value > 100` case. | |
492 | ||
493 | To see what happens when a `should_panic` test with an `expected` message | |
494 | fails, let’s again introduce a bug into our code by swapping the bodies of the | |
495 | `if value < 1` and the `else if value > 100` blocks: | |
496 | ||
497 | ```rust,ignore,not_desired_behavior | |
74b04a01 | 498 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-09-guess-with-panic-msg-bug/src/lib.rs:here}} |
13cf67c4 XL |
499 | ``` |
500 | ||
501 | This time when we run the `should_panic` test, it will fail: | |
502 | ||
f035d41b | 503 | ```console |
74b04a01 | 504 | {{#include ../listings/ch11-writing-automated-tests/no-listing-09-guess-with-panic-msg-bug/output.txt}} |
13cf67c4 XL |
505 | ``` |
506 | ||
507 | The failure message indicates that this test did indeed panic as we expected, | |
508 | but the panic message did not include the expected string `'Guess value must be | |
509 | less than or equal to 100'`. The panic message that we did get in this case was | |
510 | `Guess value must be greater than or equal to 1, got 200.` Now we can start | |
511 | figuring out where our bug is! | |
512 | ||
9fa01778 | 513 | ### Using `Result<T, E>` in Tests |
13cf67c4 XL |
514 | |
515 | So far, we’ve written tests that panic when they fail. We can also write tests | |
9fa01778 | 516 | that use `Result<T, E>`! Here’s the test from Listing 11-1, rewritten to use |
532ac7d7 | 517 | `Result<T, E>` and return an `Err` instead of panicking: |
13cf67c4 | 518 | |
fc512014 XL |
519 | ```rust,noplayground |
520 | {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-10-result-in-tests/src/lib.rs}} | |
13cf67c4 XL |
521 | ``` |
522 | ||
9fa01778 | 523 | The `it_works` function now has a return type, `Result<(), String>`. In the |
532ac7d7 | 524 | body of the function, rather than calling the `assert_eq!` macro, we return |
9fa01778 | 525 | `Ok(())` when the test passes and an `Err` with a `String` inside when the test |
532ac7d7 | 526 | fails. |
9fa01778 | 527 | |
532ac7d7 XL |
528 | Writing tests so they return a `Result<T, E>` enables you to use the question |
529 | mark operator in the body of tests, which can be a convenient way to write | |
530 | tests that should fail if any operation within them returns an `Err` variant. | |
531 | ||
532 | You can’t use the `#[should_panic]` annotation on tests that use `Result<T, | |
a2a8927a XL |
533 | E>`. To assert that an operation returns an `Err` variant, *don’t* use the |
534 | question mark operator on the `Result<T, E>` value. Instead, use | |
535 | `assert!(value.is_err())`. | |
13cf67c4 XL |
536 | |
537 | Now that you know several ways to write tests, let’s look at what is happening | |
538 | when we run our tests and explore the different options we can use with `cargo | |
539 | test`. | |
9fa01778 XL |
540 | |
541 | [concatenation-with-the--operator-or-the-format-macro]: | |
542 | ch08-02-strings.html#concatenation-with-the--operator-or-the-format-macro | |
543 | [controlling-how-tests-are-run]: | |
544 | ch11-02-running-tests.html#controlling-how-tests-are-run | |
545 | [derivable-traits]: appendix-03-derivable-traits.html | |
546 | [doc-comments]: ch14-02-publishing-to-crates-io.html#documentation-comments-as-tests | |
416331ca | 547 | [paths-for-referring-to-an-item-in-the-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html |