1228 lines
68 KiB
HTML
1228 lines
68 KiB
HTML
<!DOCTYPE HTML>
|
||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||
<head>
|
||
<!-- Book generated using mdBook -->
|
||
<meta charset="UTF-8">
|
||
<title>How to Write Tests - The Rust Programming Language</title>
|
||
|
||
|
||
<!-- Custom HTML head -->
|
||
|
||
<meta name="description" content="">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="theme-color" content="#ffffff">
|
||
|
||
<link rel="icon" href="favicon-de23e50b.svg">
|
||
<link rel="shortcut icon" href="favicon-8114d1fc.png">
|
||
<link rel="stylesheet" href="css/variables-8adf115d.css">
|
||
<link rel="stylesheet" href="css/general-0392ca55.css">
|
||
<link rel="stylesheet" href="css/chrome-fc474251.css">
|
||
<link rel="stylesheet" href="css/print-9e4910d8.css" media="print">
|
||
|
||
<!-- Fonts -->
|
||
<link rel="stylesheet" href="fonts/fonts-9644e21d.css">
|
||
|
||
<!-- Highlight.js Stylesheets -->
|
||
<link rel="stylesheet" id="mdbook-highlight-css" href="highlight-493f70e1.css">
|
||
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="tomorrow-night-4c0ae647.css">
|
||
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="ayu-highlight-3fdfc3ac.css">
|
||
|
||
<!-- Custom theme stylesheets -->
|
||
<link rel="stylesheet" href="ferris-d33b75bf.css">
|
||
<link rel="stylesheet" href="theme/2018-edition-4e126c62.css">
|
||
<link rel="stylesheet" href="theme/semantic-notes-9b5766c0.css">
|
||
<link rel="stylesheet" href="theme/listing-cab26221.css">
|
||
|
||
|
||
<!-- Provide site root and default themes to javascript -->
|
||
<script>
|
||
const path_to_root = "";
|
||
const default_light_theme = "light";
|
||
const default_dark_theme = "navy";
|
||
window.path_to_searchindex_js = "searchindex-6a1da8cc.js";
|
||
</script>
|
||
<!-- Start loading toc.js asap -->
|
||
<script src="toc-0e4ce700.js"></script>
|
||
</head>
|
||
<body>
|
||
<div id="mdbook-help-container">
|
||
<div id="mdbook-help-popup">
|
||
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
|
||
<div>
|
||
<p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>
|
||
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
|
||
<p>Press <kbd>?</kbd> to show this help</p>
|
||
<p>Press <kbd>Esc</kbd> to hide this help</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="mdbook-body-container">
|
||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||
<script>
|
||
try {
|
||
let theme = localStorage.getItem('mdbook-theme');
|
||
let sidebar = localStorage.getItem('mdbook-sidebar');
|
||
|
||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||
}
|
||
|
||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||
}
|
||
} catch (e) { }
|
||
</script>
|
||
|
||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||
<script>
|
||
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
|
||
let theme;
|
||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||
const html = document.documentElement;
|
||
html.classList.remove('light')
|
||
html.classList.add(theme);
|
||
html.classList.add("js");
|
||
</script>
|
||
|
||
<input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden">
|
||
|
||
<!-- Hide / unhide sidebar before it is displayed -->
|
||
<script>
|
||
let sidebar = null;
|
||
const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor");
|
||
if (document.body.clientWidth >= 1080) {
|
||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||
sidebar = sidebar || 'visible';
|
||
} else {
|
||
sidebar = 'hidden';
|
||
sidebar_toggle.checked = false;
|
||
}
|
||
if (sidebar === 'visible') {
|
||
sidebar_toggle.checked = true;
|
||
} else {
|
||
html.classList.remove('sidebar-visible');
|
||
}
|
||
</script>
|
||
|
||
<nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents">
|
||
<!-- populated by js -->
|
||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||
<noscript>
|
||
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
|
||
</noscript>
|
||
<div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
|
||
<div class="sidebar-resize-indicator"></div>
|
||
</div>
|
||
</nav>
|
||
|
||
<div id="mdbook-page-wrapper" class="page-wrapper">
|
||
|
||
<div class="page">
|
||
<div id="mdbook-menu-bar-hover-placeholder"></div>
|
||
<div id="mdbook-menu-bar" class="menu-bar sticky">
|
||
<div class="left-buttons">
|
||
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
|
||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
|
||
</label>
|
||
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
|
||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
|
||
</button>
|
||
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
|
||
</ul>
|
||
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
|
||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
|
||
</button>
|
||
</div>
|
||
|
||
<h1 class="menu-title">The Rust Programming Language</h1>
|
||
|
||
<div class="right-buttons">
|
||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
|
||
</a>
|
||
<a href="https://github.com/rust-lang/book" title="Git repository" aria-label="Git repository">
|
||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span>
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div id="mdbook-search-wrapper" class="hidden">
|
||
<form id="mdbook-searchbar-outer" class="searchbar-outer">
|
||
<div class="search-wrapper">
|
||
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
|
||
<div class="spinner-wrapper">
|
||
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
|
||
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
|
||
<ul id="mdbook-searchresults">
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||
<script>
|
||
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
|
||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||
});
|
||
</script>
|
||
|
||
<div id="mdbook-content" class="content">
|
||
<main>
|
||
<h2 id="how-to-write-tests"><a class="header" href="#how-to-write-tests">How to Write Tests</a></h2>
|
||
<p><em>Tests</em> are Rust functions that verify that the non-test code is functioning in
|
||
the expected manner. The bodies of test functions typically perform these three
|
||
actions:</p>
|
||
<ul>
|
||
<li>Set up any needed data or state.</li>
|
||
<li>Run the code you want to test.</li>
|
||
<li>Assert that the results are what you expect.</li>
|
||
</ul>
|
||
<p>Let’s look at the features Rust provides specifically for writing tests that
|
||
take these actions, which include the <code>test</code> attribute, a few macros, and the
|
||
<code>should_panic</code> attribute.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="the-anatomy-of-a-test-function"></a></p>
|
||
<h3 id="structuring-test-functions"><a class="header" href="#structuring-test-functions">Structuring Test Functions</a></h3>
|
||
<p>At its simplest, a test in Rust is a function that’s annotated with the <code>test</code>
|
||
attribute. Attributes are metadata about pieces of Rust code; one example is
|
||
the <code>derive</code> attribute we used with structs in Chapter 5. To change a function
|
||
into a test function, add <code>#[test]</code> on the line before <code>fn</code>. When you run your
|
||
tests with the <code>cargo test</code> command, Rust builds a test runner binary that runs
|
||
the annotated functions and reports on whether each test function passes or
|
||
fails.</p>
|
||
<p>Whenever we make a new library project with Cargo, a test module with a test
|
||
function in it is automatically generated for us. This module gives you a
|
||
template for writing your tests so that you don’t have to look up the exact
|
||
structure and syntax every time you start a new project. You can add as many
|
||
additional test functions and as many test modules as you want!</p>
|
||
<p>We’ll explore some aspects of how tests work by experimenting with the template
|
||
test before we actually test any code. Then, we’ll write some real-world tests
|
||
that call some code that we’ve written and assert that its behavior is correct.</p>
|
||
<p>Let’s create a new library project called <code>adder</code> that will add two numbers:</p>
|
||
<pre><code class="language-console">$ cargo new adder --lib
|
||
Created library `adder` project
|
||
$ cd adder
|
||
</code></pre>
|
||
<p>The contents of the <em>src/lib.rs</em> file in your <code>adder</code> library should look like
|
||
Listing 11-1.</p>
|
||
<figure class="listing" id="listing-11-1">
|
||
<span class="file-name">Filename: src/lib.rs</span>
|
||
<!-- manual-regeneration
|
||
cd listings/ch11-writing-automated-tests
|
||
rm -rf listing-11-01
|
||
cargo new listing-11-01 --lib --name adder
|
||
cd listing-11-01
|
||
echo "$ cargo test" > output.txt
|
||
RUSTFLAGS="-A unused_variables -A dead_code" RUST_TEST_THREADS=1 cargo test >> output.txt 2>&1
|
||
git diff output.txt # commit any relevant changes; discard irrelevant ones
|
||
cd ../../..
|
||
-->
|
||
<pre><code class="language-rust noplayground">pub fn add(left: u64, right: u64) -> u64 {
|
||
left + right
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn it_works() {
|
||
let result = add(2, 2);
|
||
assert_eq!(result, 4);
|
||
}
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-11-1">Listing 11-1</a>: The code generated automatically by <code>cargo new</code></figcaption>
|
||
</figure>
|
||
<p>The file starts with an example <code>add</code> function so that we have something to
|
||
test.</p>
|
||
<p>For now, let’s focus solely on the <code>it_works</code> function. Note the <code>#[test]</code>
|
||
annotation: This attribute indicates this is a test function, so the test
|
||
runner knows to treat this function as a test. We might also have non-test
|
||
functions in the <code>tests</code> module to help set up common scenarios or perform
|
||
common operations, so we always need to indicate which functions are tests.</p>
|
||
<p>The example function body uses the <code>assert_eq!</code> macro to assert that <code>result</code>,
|
||
which contains the result of calling <code>add</code> with 2 and 2, equals 4. This
|
||
assertion serves as an example of the format for a typical test. Let’s run it
|
||
to see that this test passes.</p>
|
||
<p>The <code>cargo test</code> command runs all tests in our project, as shown in Listing
|
||
11-2.</p>
|
||
<figure class="listing" id="listing-11-2">
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling adder v0.1.0 (file:///projects/adder)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.57s
|
||
Running unittests src/lib.rs (target/debug/deps/adder-01ad14159ff659ab)
|
||
|
||
running 1 test
|
||
test tests::it_works ... ok
|
||
|
||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
Doc-tests adder
|
||
|
||
running 0 tests
|
||
|
||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
</code></pre>
|
||
<figcaption><a href="#listing-11-2">Listing 11-2</a>: The output from running the automatically generated test</figcaption>
|
||
</figure>
|
||
<p>Cargo compiled and ran the test. We see the line <code>running 1 test</code>. The next
|
||
line shows the name of the generated test function, called <code>tests::it_works</code>,
|
||
and that the result of running that test is <code>ok</code>. The overall summary <code>test result: ok.</code> means that all the tests passed, and the portion that reads <code>1 passed; 0 failed</code> totals the number of tests that passed or failed.</p>
|
||
<p>It’s possible to mark a test as ignored so that it doesn’t run in a particular
|
||
instance; we’ll cover that in the <a href="ch11-02-running-tests.html#ignoring-tests-unless-specifically-requested">“Ignoring Tests Unless Specifically
|
||
Requested”</a><!-- ignore --> section later in this chapter. Because we
|
||
haven’t done that here, the summary shows <code>0 ignored</code>. We can also pass an
|
||
argument to the <code>cargo test</code> command to run only tests whose name matches a
|
||
string; this is called <em>filtering</em>, and we’ll cover it in the <a href="ch11-02-running-tests.html#running-a-subset-of-tests-by-name">“Running a
|
||
Subset of Tests by Name”</a><!-- ignore --> section. Here, we haven’t
|
||
filtered the tests being run, so the end of the summary shows <code>0 filtered out</code>.</p>
|
||
<p>The <code>0 measured</code> statistic is for benchmark tests that measure performance.
|
||
Benchmark tests are, as of this writing, only available in nightly Rust. See
|
||
<a href="../unstable-book/library-features/test.html">the documentation about benchmark tests</a> to learn more.</p>
|
||
<p>The next part of the test output starting at <code>Doc-tests adder</code> is for the
|
||
results of any documentation tests. We don’t have any documentation tests yet,
|
||
but Rust can compile any code examples that appear in our API documentation.
|
||
This feature helps keep your docs and your code in sync! We’ll discuss how to
|
||
write documentation tests in the <a href="ch14-02-publishing-to-crates-io.html#documentation-comments-as-tests">“Documentation Comments as
|
||
Tests”</a><!-- ignore --> section of Chapter 14. For now, we’ll
|
||
ignore the <code>Doc-tests</code> output.</p>
|
||
<p>Let’s start to customize the test to our own needs. First, change the name of
|
||
the <code>it_works</code> function to a different name, such as <code>exploration</code>, like so:</p>
|
||
<p><span class="filename">Filename: src/lib.rs</span></p>
|
||
<pre><code class="language-rust noplayground">pub fn add(left: u64, right: u64) -> u64 {
|
||
left + right
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn exploration() {
|
||
let result = add(2, 2);
|
||
assert_eq!(result, 4);
|
||
}
|
||
}</code></pre>
|
||
<p>Then, run <code>cargo test</code> again. The output now shows <code>exploration</code> instead of
|
||
<code>it_works</code>:</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling adder v0.1.0 (file:///projects/adder)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.59s
|
||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||
|
||
running 1 test
|
||
test tests::exploration ... ok
|
||
|
||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
Doc-tests adder
|
||
|
||
running 0 tests
|
||
|
||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
</code></pre>
|
||
<p>Now we’ll add another test, but this time we’ll make a test that fails! Tests
|
||
fail when something in the test function panics. Each test is run in a new
|
||
thread, and when the main thread sees that a test thread has died, the test is
|
||
marked as failed. In Chapter 9, we talked about how the simplest way to panic
|
||
is to call the <code>panic!</code> macro. Enter the new test as a function named
|
||
<code>another</code>, so your <em>src/lib.rs</em> file looks like Listing 11-3.</p>
|
||
<figure class="listing" id="listing-11-3">
|
||
<span class="file-name">Filename: src/lib.rs</span>
|
||
<pre><code class="language-rust panics noplayground">pub fn add(left: u64, right: u64) -> u64 {
|
||
left + right
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn exploration() {
|
||
let result = add(2, 2);
|
||
assert_eq!(result, 4);
|
||
}
|
||
|
||
#[test]
|
||
fn another() {
|
||
panic!("Make this test fail");
|
||
}
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-11-3">Listing 11-3</a>: Adding a second test that will fail because we call the <code>panic!</code> macro</figcaption>
|
||
</figure>
|
||
<p>Run the tests again using <code>cargo test</code>. The output should look like Listing
|
||
11-4, which shows that our <code>exploration</code> test passed and <code>another</code> failed.</p>
|
||
<figure class="listing" id="listing-11-4">
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling adder v0.1.0 (file:///projects/adder)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.72s
|
||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||
|
||
running 2 tests
|
||
test tests::another ... FAILED
|
||
test tests::exploration ... ok
|
||
|
||
failures:
|
||
|
||
---- tests::another stdout ----
|
||
|
||
thread 'tests::another' panicked at src/lib.rs:17:9:
|
||
Make this test fail
|
||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||
|
||
|
||
failures:
|
||
tests::another
|
||
|
||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
error: test failed, to rerun pass `--lib`
|
||
</code></pre>
|
||
<figcaption><a href="#listing-11-4">Listing 11-4</a>: Test results when one test passes and one test fails</figcaption>
|
||
</figure>
|
||
<!-- manual-regeneration
|
||
rg panicked listings/ch11-writing-automated-tests/listing-11-03/output.txt
|
||
check the line number of the panic matches the line number in the following paragraph
|
||
-->
|
||
<p>Instead of <code>ok</code>, the line <code>test tests::another</code> shows <code>FAILED</code>. Two new
|
||
sections appear between the individual results and the summary: The first
|
||
displays the detailed reason for each test failure. In this case, we get the
|
||
details that <code>tests::another</code> failed because it panicked with the message <code>Make this test fail</code> on line 17 in the <em>src/lib.rs</em> file. The next section lists
|
||
just the names of all the failing tests, which is useful when there are lots of
|
||
tests and lots of detailed failing test output. We can use the name of a
|
||
failing test to run just that test to debug it more easily; we’ll talk more
|
||
about ways to run tests in the <a href="ch11-02-running-tests.html#controlling-how-tests-are-run">“Controlling How Tests Are
|
||
Run”</a><!-- ignore --> section.</p>
|
||
<p>The summary line displays at the end: Overall, our test result is <code>FAILED</code>. We
|
||
had one test pass and one test fail.</p>
|
||
<p>Now that you’ve seen what the test results look like in different scenarios,
|
||
let’s look at some macros other than <code>panic!</code> that are useful in tests.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="checking-results-with-the-assert-macro"></a></p>
|
||
<h3 id="checking-results-with-assert"><a class="header" href="#checking-results-with-assert">Checking Results with <code>assert!</code></a></h3>
|
||
<p>The <code>assert!</code> macro, provided by the standard library, is useful when you want
|
||
to ensure that some condition in a test evaluates to <code>true</code>. We give the
|
||
<code>assert!</code> macro an argument that evaluates to a Boolean. If the value is
|
||
<code>true</code>, nothing happens and the test passes. If the value is <code>false</code>, the
|
||
<code>assert!</code> macro calls <code>panic!</code> to cause the test to fail. Using the <code>assert!</code>
|
||
macro helps us check that our code is functioning in the way we intend.</p>
|
||
<p>In Chapter 5, Listing 5-15, we used a <code>Rectangle</code> struct and a <code>can_hold</code>
|
||
method, which are repeated here in Listing 11-5. Let’s put this code in the
|
||
<em>src/lib.rs</em> file, then write some tests for it using the <code>assert!</code> macro.</p>
|
||
<figure class="listing" id="listing-11-5">
|
||
<span class="file-name">Filename: src/lib.rs</span>
|
||
<pre><code class="language-rust noplayground">#[derive(Debug)]
|
||
struct Rectangle {
|
||
width: u32,
|
||
height: u32,
|
||
}
|
||
|
||
impl Rectangle {
|
||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||
self.width > other.width && self.height > other.height
|
||
}
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-11-5">Listing 11-5</a>: The <code>Rectangle</code> struct and its <code>can_hold</code> method from Chapter 5</figcaption>
|
||
</figure>
|
||
<p>The <code>can_hold</code> method returns a Boolean, which means it’s a perfect use case
|
||
for the <code>assert!</code> macro. In Listing 11-6, we write a test that exercises the
|
||
<code>can_hold</code> method by creating a <code>Rectangle</code> instance that has a width of 8 and
|
||
a height of 7 and asserting that it can hold another <code>Rectangle</code> instance that
|
||
has a width of 5 and a height of 1.</p>
|
||
<figure class="listing" id="listing-11-6">
|
||
<span class="file-name">Filename: src/lib.rs</span>
|
||
<pre><code class="language-rust noplayground"><span class="boring">#[derive(Debug)]
|
||
</span><span class="boring">struct Rectangle {
|
||
</span><span class="boring"> width: u32,
|
||
</span><span class="boring"> height: u32,
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span><span class="boring">impl Rectangle {
|
||
</span><span class="boring"> fn can_hold(&self, other: &Rectangle) -> bool {
|
||
</span><span class="boring"> self.width > other.width && self.height > other.height
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span>#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn larger_can_hold_smaller() {
|
||
let larger = Rectangle {
|
||
width: 8,
|
||
height: 7,
|
||
};
|
||
let smaller = Rectangle {
|
||
width: 5,
|
||
height: 1,
|
||
};
|
||
|
||
assert!(larger.can_hold(&smaller));
|
||
}
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-11-6">Listing 11-6</a>: A test for <code>can_hold</code> that checks whether a larger rectangle can indeed hold a smaller rectangle</figcaption>
|
||
</figure>
|
||
<p>Note the <code>use super::*;</code> line inside the <code>tests</code> module. The <code>tests</code> module is
|
||
a regular module that follows the usual visibility rules we covered in Chapter
|
||
7 in the <a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html">“Paths for Referring to an Item in the Module
|
||
Tree”</a><!-- ignore -->
|
||
section. Because the <code>tests</code> module is an inner module, we need to bring the
|
||
code under test in the outer module into the scope of the inner module. We use
|
||
a glob here, so anything we define in the outer module is available to this
|
||
<code>tests</code> module.</p>
|
||
<p>We’ve named our test <code>larger_can_hold_smaller</code>, and we’ve created the two
|
||
<code>Rectangle</code> instances that we need. Then, we called the <code>assert!</code> macro and
|
||
passed it the result of calling <code>larger.can_hold(&smaller)</code>. This expression is
|
||
supposed to return <code>true</code>, so our test should pass. Let’s find out!</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling rectangle v0.1.0 (file:///projects/rectangle)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
|
||
Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
|
||
|
||
running 1 test
|
||
test tests::larger_can_hold_smaller ... ok
|
||
|
||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
Doc-tests rectangle
|
||
|
||
running 0 tests
|
||
|
||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
</code></pre>
|
||
<p>It does pass! Let’s add another test, this time asserting that a smaller
|
||
rectangle cannot hold a larger rectangle:</p>
|
||
<p><span class="filename">Filename: src/lib.rs</span></p>
|
||
<pre><code class="language-rust noplayground"><span class="boring">#[derive(Debug)]
|
||
</span><span class="boring">struct Rectangle {
|
||
</span><span class="boring"> width: u32,
|
||
</span><span class="boring"> height: u32,
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span><span class="boring">impl Rectangle {
|
||
</span><span class="boring"> fn can_hold(&self, other: &Rectangle) -> bool {
|
||
</span><span class="boring"> self.width > other.width && self.height > other.height
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span>#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn larger_can_hold_smaller() {
|
||
// --snip--
|
||
<span class="boring"> let larger = Rectangle {
|
||
</span><span class="boring"> width: 8,
|
||
</span><span class="boring"> height: 7,
|
||
</span><span class="boring"> };
|
||
</span><span class="boring"> let smaller = Rectangle {
|
||
</span><span class="boring"> width: 5,
|
||
</span><span class="boring"> height: 1,
|
||
</span><span class="boring"> };
|
||
</span><span class="boring">
|
||
</span><span class="boring"> assert!(larger.can_hold(&smaller));
|
||
</span> }
|
||
|
||
#[test]
|
||
fn smaller_cannot_hold_larger() {
|
||
let larger = Rectangle {
|
||
width: 8,
|
||
height: 7,
|
||
};
|
||
let smaller = Rectangle {
|
||
width: 5,
|
||
height: 1,
|
||
};
|
||
|
||
assert!(!smaller.can_hold(&larger));
|
||
}
|
||
}</code></pre>
|
||
<p>Because the correct result of the <code>can_hold</code> function in this case is <code>false</code>,
|
||
we need to negate that result before we pass it to the <code>assert!</code> macro. As a
|
||
result, our test will pass if <code>can_hold</code> returns <code>false</code>:</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling rectangle v0.1.0 (file:///projects/rectangle)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
|
||
Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
|
||
|
||
running 2 tests
|
||
test tests::larger_can_hold_smaller ... ok
|
||
test tests::smaller_cannot_hold_larger ... ok
|
||
|
||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
Doc-tests rectangle
|
||
|
||
running 0 tests
|
||
|
||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
</code></pre>
|
||
<p>Two tests that pass! Now let’s see what happens to our test results when we
|
||
introduce a bug in our code. We’ll change the implementation of the <code>can_hold</code>
|
||
method by replacing the greater-than sign (<code>></code>) with a less-than sign (<code><</code>)
|
||
when it compares the widths:</p>
|
||
<pre><code class="language-rust not_desired_behavior noplayground"><span class="boring">#[derive(Debug)]
|
||
</span><span class="boring">struct Rectangle {
|
||
</span><span class="boring"> width: u32,
|
||
</span><span class="boring"> height: u32,
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span>// --snip--
|
||
impl Rectangle {
|
||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||
self.width < other.width && self.height > other.height
|
||
}
|
||
}
|
||
<span class="boring">
|
||
</span><span class="boring">#[cfg(test)]
|
||
</span><span class="boring">mod tests {
|
||
</span><span class="boring"> use super::*;
|
||
</span><span class="boring">
|
||
</span><span class="boring"> #[test]
|
||
</span><span class="boring"> fn larger_can_hold_smaller() {
|
||
</span><span class="boring"> let larger = Rectangle {
|
||
</span><span class="boring"> width: 8,
|
||
</span><span class="boring"> height: 7,
|
||
</span><span class="boring"> };
|
||
</span><span class="boring"> let smaller = Rectangle {
|
||
</span><span class="boring"> width: 5,
|
||
</span><span class="boring"> height: 1,
|
||
</span><span class="boring"> };
|
||
</span><span class="boring">
|
||
</span><span class="boring"> assert!(larger.can_hold(&smaller));
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">
|
||
</span><span class="boring"> #[test]
|
||
</span><span class="boring"> fn smaller_cannot_hold_larger() {
|
||
</span><span class="boring"> let larger = Rectangle {
|
||
</span><span class="boring"> width: 8,
|
||
</span><span class="boring"> height: 7,
|
||
</span><span class="boring"> };
|
||
</span><span class="boring"> let smaller = Rectangle {
|
||
</span><span class="boring"> width: 5,
|
||
</span><span class="boring"> height: 1,
|
||
</span><span class="boring"> };
|
||
</span><span class="boring">
|
||
</span><span class="boring"> assert!(!smaller.can_hold(&larger));
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}</span></code></pre>
|
||
<p>Running the tests now produces the following:</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling rectangle v0.1.0 (file:///projects/rectangle)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
|
||
Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)
|
||
|
||
running 2 tests
|
||
test tests::larger_can_hold_smaller ... FAILED
|
||
test tests::smaller_cannot_hold_larger ... ok
|
||
|
||
failures:
|
||
|
||
---- tests::larger_can_hold_smaller stdout ----
|
||
|
||
thread 'tests::larger_can_hold_smaller' panicked at src/lib.rs:28:9:
|
||
assertion failed: larger.can_hold(&smaller)
|
||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||
|
||
|
||
failures:
|
||
tests::larger_can_hold_smaller
|
||
|
||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
error: test failed, to rerun pass `--lib`
|
||
</code></pre>
|
||
<p>Our tests caught the bug! Because <code>larger.width</code> is <code>8</code> and <code>smaller.width</code> is
|
||
<code>5</code>, the comparison of the widths in <code>can_hold</code> now returns <code>false</code>: 8 is not
|
||
less than 5.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="testing-equality-with-the-assert_eq-and-assert_ne-macros"></a></p>
|
||
<h3 id="testing-equality-with-assert_eq-and-assert_ne"><a class="header" href="#testing-equality-with-assert_eq-and-assert_ne">Testing Equality with <code>assert_eq!</code> and <code>assert_ne!</code></a></h3>
|
||
<p>A common way to verify functionality is to test for equality between the result
|
||
of the code under test and the value you expect the code to return. You could
|
||
do this by using the <code>assert!</code> macro and passing it an expression using the
|
||
<code>==</code> operator. However, this is such a common test that the standard library
|
||
provides a pair of macros—<code>assert_eq!</code> and <code>assert_ne!</code>—to perform this test
|
||
more conveniently. These macros compare two arguments for equality or
|
||
inequality, respectively. They’ll also print the two values if the assertion
|
||
fails, which makes it easier to see <em>why</em> the test failed; conversely, the
|
||
<code>assert!</code> macro only indicates that it got a <code>false</code> value for the <code>==</code>
|
||
expression, without printing the values that led to the <code>false</code> value.</p>
|
||
<p>In Listing 11-7, we write a function named <code>add_two</code> that adds <code>2</code> to its
|
||
parameter, and then we test this function using the <code>assert_eq!</code> macro.</p>
|
||
<figure class="listing" id="listing-11-7">
|
||
<span class="file-name">Filename: src/lib.rs</span>
|
||
<pre><code class="language-rust noplayground">pub fn add_two(a: u64) -> u64 {
|
||
a + 2
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn it_adds_two() {
|
||
let result = add_two(2);
|
||
assert_eq!(result, 4);
|
||
}
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-11-7">Listing 11-7</a>: Testing the function <code>add_two</code> using the <code>assert_eq!</code> macro</figcaption>
|
||
</figure>
|
||
<p>Let’s check that it passes!</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling adder v0.1.0 (file:///projects/adder)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.58s
|
||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||
|
||
running 1 test
|
||
test tests::it_adds_two ... ok
|
||
|
||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
Doc-tests adder
|
||
|
||
running 0 tests
|
||
|
||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
</code></pre>
|
||
<p>We create a variable named <code>result</code> that holds the result of calling
|
||
<code>add_two(2)</code>. Then, we pass <code>result</code> and <code>4</code> as the arguments to the
|
||
<code>assert_eq!</code> macro. The output line for this test is <code>test tests::it_adds_two ... ok</code>, and the <code>ok</code> text indicates that our test passed!</p>
|
||
<p>Let’s introduce a bug into our code to see what <code>assert_eq!</code> looks like when it
|
||
fails. Change the implementation of the <code>add_two</code> function to instead add <code>3</code>:</p>
|
||
<pre><code class="language-rust not_desired_behavior noplayground">pub fn add_two(a: u64) -> u64 {
|
||
a + 3
|
||
}
|
||
<span class="boring">
|
||
</span><span class="boring">#[cfg(test)]
|
||
</span><span class="boring">mod tests {
|
||
</span><span class="boring"> use super::*;
|
||
</span><span class="boring">
|
||
</span><span class="boring"> #[test]
|
||
</span><span class="boring"> fn it_adds_two() {
|
||
</span><span class="boring"> let result = add_two(2);
|
||
</span><span class="boring"> assert_eq!(result, 4);
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}</span></code></pre>
|
||
<p>Run the tests again:</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling adder v0.1.0 (file:///projects/adder)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
|
||
Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)
|
||
|
||
running 1 test
|
||
test tests::it_adds_two ... FAILED
|
||
|
||
failures:
|
||
|
||
---- tests::it_adds_two stdout ----
|
||
|
||
thread 'tests::it_adds_two' panicked at src/lib.rs:12:9:
|
||
assertion `left == right` failed
|
||
left: 5
|
||
right: 4
|
||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||
|
||
|
||
failures:
|
||
tests::it_adds_two
|
||
|
||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
error: test failed, to rerun pass `--lib`
|
||
</code></pre>
|
||
<p>Our test caught the bug! The <code>tests::it_adds_two</code> test failed, and the message
|
||
tells us that the assertion that failed was <code>left == right</code> and what the <code>left</code>
|
||
and <code>right</code> values are. This message helps us start debugging: The <code>left</code>
|
||
argument, where we had the result of calling <code>add_two(2)</code>, was <code>5</code>, but the
|
||
<code>right</code> argument was <code>4</code>. You can imagine that this would be especially helpful
|
||
when we have a lot of tests going on.</p>
|
||
<p>Note that in some languages and test frameworks, the parameters to equality
|
||
assertion functions are called <code>expected</code> and <code>actual</code>, and the order in which
|
||
we specify the arguments matters. However, in Rust, they’re called <code>left</code> and
|
||
<code>right</code>, and the order in which we specify the value we expect and the value
|
||
the code produces doesn’t matter. We could write the assertion in this test as
|
||
<code>assert_eq!(4, result)</code>, which would result in the same failure message that
|
||
displays <code>assertion `left == right` failed</code>.</p>
|
||
<p>The <code>assert_ne!</code> macro will pass if the two values we give it are not equal and
|
||
will fail if they are equal. This macro is most useful for cases when we’re not
|
||
sure what a value <em>will</em> be, but we know what the value definitely <em>shouldn’t</em>
|
||
be. For example, if we’re testing a function that is guaranteed to change its
|
||
input in some way, but the way in which the input is changed depends on the day
|
||
of the week that we run our tests, the best thing to assert might be that the
|
||
output of the function is not equal to the input.</p>
|
||
<p>Under the surface, the <code>assert_eq!</code> and <code>assert_ne!</code> macros use the operators
|
||
<code>==</code> and <code>!=</code>, respectively. When the assertions fail, these macros print their
|
||
arguments using debug formatting, which means the values being compared must
|
||
implement the <code>PartialEq</code> and <code>Debug</code> traits. All primitive types and most of
|
||
the standard library types implement these traits. For structs and enums that
|
||
you define yourself, you’ll need to implement <code>PartialEq</code> to assert equality of
|
||
those types. You’ll also need to implement <code>Debug</code> to print the values when the
|
||
assertion fails. Because both traits are derivable traits, as mentioned in
|
||
Listing 5-12 in Chapter 5, this is usually as straightforward as adding the
|
||
<code>#[derive(PartialEq, Debug)]</code> annotation to your struct or enum definition. See
|
||
Appendix C, <a href="appendix-03-derivable-traits.html">“Derivable Traits,”</a><!-- ignore --> for more
|
||
details about these and other derivable traits.</p>
|
||
<h3 id="adding-custom-failure-messages"><a class="header" href="#adding-custom-failure-messages">Adding Custom Failure Messages</a></h3>
|
||
<p>You can also add a custom message to be printed with the failure message as
|
||
optional arguments to the <code>assert!</code>, <code>assert_eq!</code>, and <code>assert_ne!</code> macros. Any
|
||
arguments specified after the required arguments are passed along to the
|
||
<code>format!</code> macro (discussed in <a href="ch08-02-strings.html#concatenating-with--or-format">“Concatenating with <code>+</code> or
|
||
<code>format!</code>”</a><!--
|
||
ignore --> in Chapter 8), so you can pass a format string that contains <code>{}</code>
|
||
placeholders and values to go in those placeholders. Custom messages are useful
|
||
for documenting what an assertion means; when a test fails, you’ll have a better
|
||
idea of what the problem is with the code.</p>
|
||
<p>For example, let’s say we have a function that greets people by name and we
|
||
want to test that the name we pass into the function appears in the output:</p>
|
||
<p><span class="filename">Filename: src/lib.rs</span></p>
|
||
<pre><code class="language-rust noplayground">pub fn greeting(name: &str) -> String {
|
||
format!("Hello {name}!")
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn greeting_contains_name() {
|
||
let result = greeting("Carol");
|
||
assert!(result.contains("Carol"));
|
||
}
|
||
}</code></pre>
|
||
<p>The requirements for this program haven’t been agreed upon yet, and we’re
|
||
pretty sure the <code>Hello</code> text at the beginning of the greeting will change. We
|
||
decided we don’t want to have to update the test when the requirements change,
|
||
so instead of checking for exact equality to the value returned from the
|
||
<code>greeting</code> function, we’ll just assert that the output contains the text of the
|
||
input parameter.</p>
|
||
<p>Now let’s introduce a bug into this code by changing <code>greeting</code> to exclude
|
||
<code>name</code> to see what the default test failure looks like:</p>
|
||
<pre><code class="language-rust not_desired_behavior noplayground">pub fn greeting(name: &str) -> String {
|
||
String::from("Hello!")
|
||
}
|
||
<span class="boring">
|
||
</span><span class="boring">#[cfg(test)]
|
||
</span><span class="boring">mod tests {
|
||
</span><span class="boring"> use super::*;
|
||
</span><span class="boring">
|
||
</span><span class="boring"> #[test]
|
||
</span><span class="boring"> fn greeting_contains_name() {
|
||
</span><span class="boring"> let result = greeting("Carol");
|
||
</span><span class="boring"> assert!(result.contains("Carol"));
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}</span></code></pre>
|
||
<p>Running this test produces the following:</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling greeter v0.1.0 (file:///projects/greeter)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.91s
|
||
Running unittests src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)
|
||
|
||
running 1 test
|
||
test tests::greeting_contains_name ... FAILED
|
||
|
||
failures:
|
||
|
||
---- tests::greeting_contains_name stdout ----
|
||
|
||
thread 'tests::greeting_contains_name' panicked at src/lib.rs:12:9:
|
||
assertion failed: result.contains("Carol")
|
||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||
|
||
|
||
failures:
|
||
tests::greeting_contains_name
|
||
|
||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
error: test failed, to rerun pass `--lib`
|
||
</code></pre>
|
||
<p>This result just indicates that the assertion failed and which line the
|
||
assertion is on. A more useful failure message would print the value from the
|
||
<code>greeting</code> function. Let’s add a custom failure message composed of a format
|
||
string with a placeholder filled in with the actual value we got from the
|
||
<code>greeting</code> function:</p>
|
||
<pre><code class="language-rust ignore"><span class="boring">pub fn greeting(name: &str) -> String {
|
||
</span><span class="boring"> String::from("Hello!")
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span><span class="boring">#[cfg(test)]
|
||
</span><span class="boring">mod tests {
|
||
</span><span class="boring"> use super::*;
|
||
</span><span class="boring">
|
||
</span> #[test]
|
||
fn greeting_contains_name() {
|
||
let result = greeting("Carol");
|
||
assert!(
|
||
result.contains("Carol"),
|
||
"Greeting did not contain name, value was `{result}`"
|
||
);
|
||
}
|
||
<span class="boring">}</span></code></pre>
|
||
<p>Now when we run the test, we’ll get a more informative error message:</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling greeter v0.1.0 (file:///projects/greeter)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.93s
|
||
Running unittests src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)
|
||
|
||
running 1 test
|
||
test tests::greeting_contains_name ... FAILED
|
||
|
||
failures:
|
||
|
||
---- tests::greeting_contains_name stdout ----
|
||
|
||
thread 'tests::greeting_contains_name' panicked at src/lib.rs:12:9:
|
||
Greeting did not contain name, value was `Hello!`
|
||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||
|
||
|
||
failures:
|
||
tests::greeting_contains_name
|
||
|
||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
error: test failed, to rerun pass `--lib`
|
||
</code></pre>
|
||
<p>We can see the value we actually got in the test output, which would help us
|
||
debug what happened instead of what we were expecting to happen.</p>
|
||
<h3 id="checking-for-panics-with-should_panic"><a class="header" href="#checking-for-panics-with-should_panic">Checking for Panics with <code>should_panic</code></a></h3>
|
||
<p>In addition to checking return values, it’s important to check that our code
|
||
handles error conditions as we expect. For example, consider the <code>Guess</code> type
|
||
that we created in Chapter 9, Listing 9-13. Other code that uses <code>Guess</code>
|
||
depends on the guarantee that <code>Guess</code> instances will contain only values
|
||
between 1 and 100. We can write a test that ensures that attempting to create a
|
||
<code>Guess</code> instance with a value outside that range panics.</p>
|
||
<p>We do this by adding the attribute <code>should_panic</code> to our test function. The
|
||
test passes if the code inside the function panics; the test fails if the code
|
||
inside the function doesn’t panic.</p>
|
||
<p>Listing 11-8 shows a test that checks that the error conditions of <code>Guess::new</code>
|
||
happen when we expect them to.</p>
|
||
<figure class="listing" id="listing-11-8">
|
||
<span class="file-name">Filename: src/lib.rs</span>
|
||
<pre><code class="language-rust noplayground">pub struct Guess {
|
||
value: i32,
|
||
}
|
||
|
||
impl Guess {
|
||
pub fn new(value: i32) -> Guess {
|
||
if value < 1 || value > 100 {
|
||
panic!("Guess value must be between 1 and 100, got {value}.");
|
||
}
|
||
|
||
Guess { value }
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
#[should_panic]
|
||
fn greater_than_100() {
|
||
Guess::new(200);
|
||
}
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-11-8">Listing 11-8</a>: Testing that a condition will cause a <code>panic!</code></figcaption>
|
||
</figure>
|
||
<p>We place the <code>#[should_panic]</code> attribute after the <code>#[test]</code> attribute and
|
||
before the test function it applies to. Let’s look at the result when this test
|
||
passes:</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.58s
|
||
Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
|
||
|
||
running 1 test
|
||
test tests::greater_than_100 - should panic ... ok
|
||
|
||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
Doc-tests guessing_game
|
||
|
||
running 0 tests
|
||
|
||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
</code></pre>
|
||
<p>Looks good! Now let’s introduce a bug in our code by removing the condition
|
||
that the <code>new</code> function will panic if the value is greater than 100:</p>
|
||
<pre><code class="language-rust not_desired_behavior noplayground"><span class="boring">pub struct Guess {
|
||
</span><span class="boring"> value: i32,
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span>// --snip--
|
||
impl Guess {
|
||
pub fn new(value: i32) -> Guess {
|
||
if value < 1 {
|
||
panic!("Guess value must be between 1 and 100, got {value}.");
|
||
}
|
||
|
||
Guess { value }
|
||
}
|
||
}
|
||
<span class="boring">
|
||
</span><span class="boring">#[cfg(test)]
|
||
</span><span class="boring">mod tests {
|
||
</span><span class="boring"> use super::*;
|
||
</span><span class="boring">
|
||
</span><span class="boring"> #[test]
|
||
</span><span class="boring"> #[should_panic]
|
||
</span><span class="boring"> fn greater_than_100() {
|
||
</span><span class="boring"> Guess::new(200);
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}</span></code></pre>
|
||
<p>When we run the test in Listing 11-8, it will fail:</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.62s
|
||
Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
|
||
|
||
running 1 test
|
||
test tests::greater_than_100 - should panic ... FAILED
|
||
|
||
failures:
|
||
|
||
---- tests::greater_than_100 stdout ----
|
||
note: test did not panic as expected at src/lib.rs:21:8
|
||
|
||
failures:
|
||
tests::greater_than_100
|
||
|
||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
error: test failed, to rerun pass `--lib`
|
||
</code></pre>
|
||
<p>We don’t get a very helpful message in this case, but when we look at the test
|
||
function, we see that it’s annotated with <code>#[should_panic]</code>. The failure we got
|
||
means that the code in the test function did not cause a panic.</p>
|
||
<p>Tests that use <code>should_panic</code> can be imprecise. A <code>should_panic</code> test would
|
||
pass even if the test panics for a different reason from the one we were
|
||
expecting. To make <code>should_panic</code> tests more precise, we can add an optional
|
||
<code>expected</code> parameter to the <code>should_panic</code> attribute. The test harness will
|
||
make sure that the failure message contains the provided text. For example,
|
||
consider the modified code for <code>Guess</code> in Listing 11-9 where the <code>new</code> function
|
||
panics with different messages depending on whether the value is too small or
|
||
too large.</p>
|
||
<figure class="listing" id="listing-11-9">
|
||
<span class="file-name">Filename: src/lib.rs</span>
|
||
<pre><code class="language-rust noplayground"><span class="boring">pub struct Guess {
|
||
</span><span class="boring"> value: i32,
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span>// --snip--
|
||
|
||
impl Guess {
|
||
pub fn new(value: i32) -> Guess {
|
||
if value < 1 {
|
||
panic!(
|
||
"Guess value must be greater than or equal to 1, got {value}."
|
||
);
|
||
} else if value > 100 {
|
||
panic!(
|
||
"Guess value must be less than or equal to 100, got {value}."
|
||
);
|
||
}
|
||
|
||
Guess { value }
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
#[should_panic(expected = "less than or equal to 100")]
|
||
fn greater_than_100() {
|
||
Guess::new(200);
|
||
}
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-11-9">Listing 11-9</a>: Testing for a <code>panic!</code> with a panic message containing a specified substring</figcaption>
|
||
</figure>
|
||
<p>This test will pass because the value we put in the <code>should_panic</code> attribute’s
|
||
<code>expected</code> parameter is a substring of the message that the <code>Guess::new</code>
|
||
function panics with. We could have specified the entire panic message that we
|
||
expect, which in this case would be <code>Guess value must be less than or equal to 100, got 200</code>. What you choose to specify depends on how much of the panic
|
||
message is unique or dynamic and how precise you want your test to be. In this
|
||
case, a substring of the panic message is enough to ensure that the code in the
|
||
test function executes the <code>else if value > 100</code> case.</p>
|
||
<p>To see what happens when a <code>should_panic</code> test with an <code>expected</code> message
|
||
fails, let’s again introduce a bug into our code by swapping the bodies of the
|
||
<code>if value < 1</code> and the <code>else if value > 100</code> blocks:</p>
|
||
<pre><code class="language-rust ignore not_desired_behavior"><span class="boring">pub struct Guess {
|
||
</span><span class="boring"> value: i32,
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span><span class="boring">impl Guess {
|
||
</span><span class="boring"> pub fn new(value: i32) -> Guess {
|
||
</span> if value < 1 {
|
||
panic!(
|
||
"Guess value must be less than or equal to 100, got {value}."
|
||
);
|
||
} else if value > 100 {
|
||
panic!(
|
||
"Guess value must be greater than or equal to 1, got {value}."
|
||
);
|
||
}
|
||
<span class="boring">
|
||
</span><span class="boring"> Guess { value }
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span><span class="boring">#[cfg(test)]
|
||
</span><span class="boring">mod tests {
|
||
</span><span class="boring"> use super::*;
|
||
</span><span class="boring">
|
||
</span><span class="boring"> #[test]
|
||
</span><span class="boring"> #[should_panic(expected = "less than or equal to 100")]
|
||
</span><span class="boring"> fn greater_than_100() {
|
||
</span><span class="boring"> Guess::new(200);
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}</span></code></pre>
|
||
<p>This time when we run the <code>should_panic</code> test, it will fail:</p>
|
||
<pre><code class="language-console">$ cargo test
|
||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
|
||
Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)
|
||
|
||
running 1 test
|
||
test tests::greater_than_100 - should panic ... FAILED
|
||
|
||
failures:
|
||
|
||
---- tests::greater_than_100 stdout ----
|
||
|
||
thread 'tests::greater_than_100' panicked at src/lib.rs:12:13:
|
||
Guess value must be greater than or equal to 1, got 200.
|
||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||
note: panic did not contain expected string
|
||
panic message: "Guess value must be greater than or equal to 1, got 200."
|
||
expected substring: "less than or equal to 100"
|
||
|
||
failures:
|
||
tests::greater_than_100
|
||
|
||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||
|
||
error: test failed, to rerun pass `--lib`
|
||
</code></pre>
|
||
<p>The failure message indicates that this test did indeed panic as we expected,
|
||
but the panic message did not include the expected string <code>less than or equal to 100</code>. The panic message that we did get in this case was <code>Guess value must be greater than or equal to 1, got 200</code>. Now we can start figuring out where
|
||
our bug is!</p>
|
||
<h3 id="using-resultt-e-in-tests"><a class="header" href="#using-resultt-e-in-tests">Using <code>Result<T, E></code> in Tests</a></h3>
|
||
<p>All of our tests so far panic when they fail. We can also write tests that use
|
||
<code>Result<T, E></code>! Here’s the test from Listing 11-1, rewritten to use <code>Result<T, E></code> and return an <code>Err</code> instead of panicking:</p>
|
||
<pre><code class="language-rust noplayground"><span class="boring">pub fn add(left: u64, right: u64) -> u64 {
|
||
</span><span class="boring"> left + right
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span>#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn it_works() -> Result<(), String> {
|
||
let result = add(2, 2);
|
||
|
||
if result == 4 {
|
||
Ok(())
|
||
} else {
|
||
Err(String::from("two plus two does not equal four"))
|
||
}
|
||
}
|
||
}</code></pre>
|
||
<p>The <code>it_works</code> function now has the <code>Result<(), String></code> return type. In the
|
||
body of the function, rather than calling the <code>assert_eq!</code> macro, we return
|
||
<code>Ok(())</code> when the test passes and an <code>Err</code> with a <code>String</code> inside when the test
|
||
fails.</p>
|
||
<p>Writing tests so that they return a <code>Result<T, E></code> enables you to use the
|
||
question mark operator in the body of tests, which can be a convenient way to
|
||
write tests that should fail if any operation within them returns an <code>Err</code>
|
||
variant.</p>
|
||
<p>You can’t use the <code>#[should_panic]</code> annotation on tests that use <code>Result<T, E></code>. To assert that an operation returns an <code>Err</code> variant, <em>don’t</em> use the
|
||
question mark operator on the <code>Result<T, E></code> value. Instead, use
|
||
<code>assert!(value.is_err())</code>.</p>
|
||
<p>Now that you know several ways to write tests, let’s look at what is happening
|
||
when we run our tests and explore the different options we can use with <code>cargo test</code>.</p>
|
||
|
||
</main>
|
||
|
||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||
<!-- Mobile navigation buttons -->
|
||
<a rel="prev" href="ch11-00-testing.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
|
||
</a>
|
||
|
||
<a rel="next prefetch" href="ch11-02-running-tests.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||
</a>
|
||
|
||
<div style="clear: both"></div>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||
<a rel="prev" href="ch11-00-testing.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
|
||
</a>
|
||
|
||
<a rel="next prefetch" href="ch11-02-running-tests.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
|
||
</a>
|
||
</nav>
|
||
|
||
</div>
|
||
|
||
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
|
||
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
|
||
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
|
||
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
|
||
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
|
||
|
||
|
||
|
||
<script>
|
||
window.playground_copyable = true;
|
||
</script>
|
||
|
||
|
||
<script src="elasticlunr-ef4e11c1.min.js"></script>
|
||
<script src="mark-09e88c2c.min.js"></script>
|
||
<script src="searcher-09f2665d.js"></script>
|
||
|
||
<script src="clipboard-1626706a.min.js"></script>
|
||
<script src="highlight-abc7f01d.js"></script>
|
||
<script src="book-c22b7243.js"></script>
|
||
|
||
<!-- Custom JS scripts -->
|
||
<script src="ferris-2317480c.js"></script>
|
||
|
||
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|