|
1 | 1 | # Documentation tests
|
2 | 2 |
|
3 |
| -Coming soon! |
| 3 | +`rustdoc` supports executing your documentation examples as tests. This makes sure |
| 4 | +that your tests are up to date and working. |
| 5 | + |
| 6 | +The basic idea is this: |
| 7 | + |
| 8 | +```rust,ignore |
| 9 | +/// # Examples |
| 10 | +/// |
| 11 | +/// ``` |
| 12 | +/// let x = 5; |
| 13 | +/// ``` |
| 14 | +``` |
| 15 | + |
| 16 | +The triple backticks start and end code blocks. If this were in a file named `foo.rs`, |
| 17 | +running `rustdoc --test foo.rs` will extract this example, and then run it as a test. |
| 18 | + |
| 19 | +There's some subtlety though! Read on for more details. |
| 20 | + |
| 21 | +## Pre-processing examples |
| 22 | + |
| 23 | +In the example above, you'll note something strange: there's no `main` |
| 24 | +function! Forcing you to write `main` for every example, no matter how small, |
| 25 | +adds friction. So `rustdoc` processes your examples slightly before |
| 26 | +running them. Here's the full algorithm rustdoc uses to preprocess examples: |
| 27 | + |
| 28 | +1. Any leading `#![foo]` attributes are left intact as crate attributes. |
| 29 | +2. Some common `allow` attributes are inserted, including |
| 30 | + `unused_variables`, `unused_assignments`, `unused_mut`, |
| 31 | + `unused_attributes`, and `dead_code`. Small examples often trigger |
| 32 | + these lints. |
| 33 | +3. If the example does not contain `extern crate`, then `extern crate |
| 34 | + <mycrate>;` is inserted (note the lack of `#[macro_use]`). |
| 35 | +4. Finally, if the example does not contain `fn main`, the remainder of the |
| 36 | + text is wrapped in `fn main() { your_code }`. |
| 37 | + |
| 38 | +For more about that caveat in rule 3, see "Documeting Macros" below. |
| 39 | + |
| 40 | +## Hiding portions of the example |
| 41 | + |
| 42 | +Sometimes, you need some setup code, or other things that would distract |
| 43 | +from your example, but are important to make the tests work. Consider |
| 44 | +an example block that looks like this: |
| 45 | + |
| 46 | +```text |
| 47 | +/// Some documentation. |
| 48 | +# fn foo() {} |
| 49 | +``` |
| 50 | + |
| 51 | +It will render like this: |
| 52 | + |
| 53 | +```rust |
| 54 | +/// Some documentation. |
| 55 | +# fn foo() {} |
| 56 | +``` |
| 57 | + |
| 58 | +Yes, that's right: you can add lines that start with `# `, and they will |
| 59 | +be hidden from the output, but will be used when compiling your code. You |
| 60 | +can use this to your advantage. In this case, documentation comments need |
| 61 | +to apply to some kind of function, so if I want to show you just a |
| 62 | +documentation comment, I need to add a little function definition below |
| 63 | +it. At the same time, it's only there to satisfy the compiler, so hiding |
| 64 | +it makes the example more clear. You can use this technique to explain |
| 65 | +longer examples in detail, while still preserving the testability of your |
| 66 | +documentation. |
| 67 | + |
| 68 | +For example, imagine that we wanted to document this code: |
| 69 | + |
| 70 | +```rust |
| 71 | +let x = 5; |
| 72 | +let y = 6; |
| 73 | +println!("{}", x + y); |
| 74 | +``` |
| 75 | + |
| 76 | +We might want the documentation to end up looking like this: |
| 77 | + |
| 78 | +> First, we set `x` to five: |
| 79 | +> |
| 80 | +> ```rust |
| 81 | +> let x = 5; |
| 82 | +> # let y = 6; |
| 83 | +> # println!("{}", x + y); |
| 84 | +> ``` |
| 85 | +> |
| 86 | +> Next, we set `y` to six: |
| 87 | +> |
| 88 | +> ```rust |
| 89 | +> # let x = 5; |
| 90 | +> let y = 6; |
| 91 | +> # println!("{}", x + y); |
| 92 | +> ``` |
| 93 | +> |
| 94 | +> Finally, we print the sum of `x` and `y`: |
| 95 | +> |
| 96 | +> ```rust |
| 97 | +> # let x = 5; |
| 98 | +> # let y = 6; |
| 99 | +> println!("{}", x + y); |
| 100 | +> ``` |
| 101 | +
|
| 102 | +To keep each code block testable, we want the whole program in each block, but |
| 103 | +we don't want the reader to see every line every time. Here's what we put in |
| 104 | +our source code: |
| 105 | +
|
| 106 | +```text |
| 107 | + First, we set `x` to five: |
| 108 | +
|
| 109 | + ```rust |
| 110 | + let x = 5; |
| 111 | + # let y = 6; |
| 112 | + # println!("{}", x + y); |
| 113 | + ``` |
| 114 | +
|
| 115 | + Next, we set `y` to six: |
| 116 | +
|
| 117 | + ```rust |
| 118 | + # let x = 5; |
| 119 | + let y = 6; |
| 120 | + # println!("{}", x + y); |
| 121 | + ``` |
| 122 | +
|
| 123 | + Finally, we print the sum of `x` and `y`: |
| 124 | +
|
| 125 | + ```rust |
| 126 | + # let x = 5; |
| 127 | + # let y = 6; |
| 128 | + println!("{}", x + y); |
| 129 | + ``` |
| 130 | +``` |
| 131 | +
|
| 132 | +By repeating all parts of the example, you can ensure that your example still |
| 133 | +compiles, while only showing the parts that are relevant to that part of your |
| 134 | +explanation. |
| 135 | + |
| 136 | +Another case where the use of `#` is handy is when you want to ignore |
| 137 | +error handling. Lets say you want the following, |
| 138 | + |
| 139 | +```rust,ignore |
| 140 | +/// use std::io; |
| 141 | +/// let mut input = String::new(); |
| 142 | +/// io::stdin().read_line(&mut input)?; |
| 143 | +``` |
| 144 | + |
| 145 | +The problem is that `?` returns a `Result<T, E>` and test functions |
| 146 | +don't return anything so this will give a mismatched types error. |
| 147 | + |
| 148 | +```rust,ignore |
| 149 | +/// A doc test using ? |
| 150 | +/// |
| 151 | +/// ``` |
| 152 | +/// use std::io; |
| 153 | +/// # fn foo() -> io::Result<()> { |
| 154 | +/// let mut input = String::new(); |
| 155 | +/// io::stdin().read_line(&mut input)?; |
| 156 | +/// # Ok(()) |
| 157 | +/// # } |
| 158 | +/// ``` |
| 159 | +# fn foo() {} |
| 160 | +``` |
| 161 | + |
| 162 | +You can get around this by wrapping the code in a function. This catches |
| 163 | +and swallows the `Result<T, E>` when running tests on the docs. This |
| 164 | +pattern appears regularly in the standard library. |
| 165 | + |
| 166 | +### Documenting macros |
| 167 | + |
| 168 | +Here’s an example of documenting a macro: |
| 169 | + |
| 170 | +```rust |
| 171 | +/// Panic with a given message unless an expression evaluates to true. |
| 172 | +/// |
| 173 | +/// # Examples |
| 174 | +/// |
| 175 | +/// ``` |
| 176 | +/// # #[macro_use] extern crate foo; |
| 177 | +/// # fn main() { |
| 178 | +/// panic_unless!(1 + 1 == 2, “Math is broken.”); |
| 179 | +/// # } |
| 180 | +/// ``` |
| 181 | +/// |
| 182 | +/// ```rust,should_panic |
| 183 | +/// # #[macro_use] extern crate foo; |
| 184 | +/// # fn main() { |
| 185 | +/// panic_unless!(true == false, “I’m broken.”); |
| 186 | +/// # } |
| 187 | +/// ``` |
| 188 | +#[macro_export] |
| 189 | +macro_rules! panic_unless { |
| 190 | + ($condition:expr, $($rest:expr),+) => ({ if ! $condition { panic!($($rest),+); } }); |
| 191 | +} |
| 192 | +# fn main() {} |
| 193 | +``` |
| 194 | + |
| 195 | +You’ll note three things: we need to add our own `extern crate` line, so that |
| 196 | +we can add the `#[macro_use]` attribute. Second, we’ll need to add our own |
| 197 | +`main()` as well (for reasons discussed above). Finally, a judicious use of |
| 198 | +`#` to comment out those two things, so they don’t show up in the output. |
| 199 | + |
| 200 | +## Attributes |
| 201 | + |
| 202 | +There are a few annotations that are useful to help `rustdoc` do the right |
| 203 | +thing when testing your code: |
| 204 | + |
| 205 | +```rust |
| 206 | +/// ```ignore |
| 207 | +/// fn foo() { |
| 208 | +/// ``` |
| 209 | +# fn foo() {} |
| 210 | +``` |
| 211 | + |
| 212 | +The `ignore` directive tells Rust to ignore your code. This is almost never |
| 213 | +what you want, as it's the most generic. Instead, consider annotating it |
| 214 | +with `text` if it's not code, or using `#`s to get a working example that |
| 215 | +only shows the part you care about. |
| 216 | + |
| 217 | +```rust |
| 218 | +/// ```should_panic |
| 219 | +/// assert!(false); |
| 220 | +/// ``` |
| 221 | +# fn foo() {} |
| 222 | +``` |
| 223 | + |
| 224 | +`should_panic` tells `rustdoc` that the code should compile correctly, but |
| 225 | +not actually pass as a test. |
| 226 | + |
| 227 | +```rust |
| 228 | +/// ```no_run |
| 229 | +/// loop { |
| 230 | +/// println!("Hello, world"); |
| 231 | +/// } |
| 232 | +/// ``` |
| 233 | +# fn foo() {} |
| 234 | +``` |
| 235 | + |
| 236 | +The `no_run` attribute will compile your code, but not run it. This is |
| 237 | +important for examples such as "Here's how to retrieve a web page," |
| 238 | +which you would want to ensure compiles, but might be run in a test |
| 239 | +environment that has no network access. |
0 commit comments