1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! Testify
//!
//!   - **Date:** December 13, 2015
//!   - **Subject:** Testing and benchmarking, and compiler attributes.
//!   - [**Audio**][mp3]
//!
//! [mp3]: https://www.podtrac.com/pts/redirect.mp3/cdn.newrustacean.com/file/newrustacean/e007.mp3
//!
//! <audio style="width: 100%" title="Testify" controls preload=metadata src="https://www.podtrac.com/pts/redirect.mp3/cdn.newrustacean.com/file/newrustacean/e007.mp3" />
//!
//! Notes
//! -----
//!
//! All about testing in Rust! In order, we take a look at:
//!
//!   - Why you need tests.
//!   - Unit tests in other (dynamically-typed) languages vs. in Rust.
//!   - How to write unit tests in Rust.
//!   - How and why to write integration tests in Rust.
//!   - How and why to use benchmarks in Rust.
//!
//! The detailed code samples for this episode are heavy on showing; because of
//! the nature of test functions, you will be best off just [reading the source]
//! rather than leaning heavily on the descriptions generated by **rustdoc**.
//! (The descriptions are still *there*, but they're much less useful than they
//! have been in previous episodes.) In particular, the `test` module here is
//! excluded because of the use of the `#[cfg(test)]` attribute marker on it.
//!
//! [reading the source]: /src/show_notes/e007.rs.html
//!
//! Because we are using the feature-gated benchmarking functionality, the
//! show notes "library" can now only be compiled with the Rust nightly (as of
//! 1.5, the version current as this episode is produced).
//!
//! One thing that isn't necessarily obvious from reading the test documentation
//! in the Rust book and Rust reference: the `extern crate test` statement needs
//! to be not in this module, but at the module (`lib.rs`) which defines the
//! library/crate; in this case, `show_notes/lib.rs`.
//!
//!
//! Links
//! -----
//!
//! - Rust Book:
//!     + [Testing][links-1]
//!     + [Attributes][links-2]
//!     + [Benchmark tests][links-3]
//! - Rust reference: [Attributes][links-4]
//! - [Diesel (Rust ORM)][links-5]
//!     + [31: Oxidizing an ORM][links-6]
//!     + [32: Bug for Bug Compatibility][links-7]
//!
//! [links-1]: https://doc.rust-lang.org/book/testing.html
//! [links-2]: https://doc.rust-lang.org/book/attributes.html
//! [links-3]: https://doc.rust-lang.org/book/benchmark-tests.html
//! [links-4]: https://doc.rust-lang.org/reference.html#attributes
//! [links-5]: https://github.com/sgrif/diesel
//! [links-6]: http://bikeshed.fm/31
//! [links-7]: http://bikeshed.fm/32
//!
//!
//! Sponsors
//! --------
//!
//!   - Chris Palmer
//!   - [Derek Morr][sponsors-2]
//!   - Luca Schmid
//!   - Micael Bergeron
//!   - Ralph Giles ("rillian")
//!   - reddraggone9
//!   - [William Roe][sponsors-7]
//!
//! [sponsors-2]: https://twitter.com/derekmorr
//! [sponsors-7]: http://willroe.me
//!
//! ### Become a sponsor
//!
//!   - <a href="https://www.patreon.com/newrustacean" rel="payment">Patreon</a>
//!   - [Venmo](https://venmo.com/chriskrycho)
//!   - [Dwolla](https://www.dwolla.com/hub/chriskrycho)
//!   - [Cash.me](https://cash.me/$chriskrycho)
//!
//!
//! Follow
//! ------
//!
//!   - New Rustacean:
//!     + Twitter: [@newrustacean](https://www.twitter.com/newrustacean)
//!       + App.net: [@newrustacean](https://alpha.app.net/newrustacean)
//!     + Email: [[email protected]](mailto:[email protected])
//!   - Chris Krycho
//!     + Twitter: [@chriskrycho](https://www.twitter.com/chriskrycho)
//!       + App.net: [@chriskrycho](https://alpha.app.net/chriskrycho)

/// A trivial function for a trivial test. See the [source](/src/show_notes/e007.rs.html)!
pub fn add(a: f64, b: f64) -> f64 {
    a + b
}

/// A trivial test of a trivial function, demonstrating `#[test]`.
///
/// This test function will not be compiled into a binary; it will *only* be
/// used with the test binary. Note that you *can* create standalone test
/// functions as long as they are marked with the `#[test]` attribute, but in
/// general, you should put it in the `test` module, see below.
#[test]
fn test_add() {
    assert_eq!(add(2.0, 2.0), 4.0);
}

/// A test module!
///
/// A few comments:
///
///   - Note the `#[cfg(test)]` attribute marker on the module.
///   - Note the distinction between `#[test]` and `#[bench]`.
#[cfg(test)]
mod tests {
    // This statement gives us access to everything in the parent module, so we
    // don't have to use the full namespace (`show_notes::e007::add`) to get
    // access to the functions we want to test.
    use super::*;

    // `Bencher` is the `struct` which has the benchmarking functionality.
    use crate::test::Bencher;

    // We'll use this for demonstrating benchmarks later.
    use std::thread::sleep;
    use std::time::Duration;

    /// Another, equally trivial, test, this one for `#[should_panic]`.
    ///
    /// In a more meaningful scenario, we might use the `#[should_panic]` attribute
    /// to verify that a given function call triggers an error under conditions
    /// where it should, e.g. if we tried to `.unwrap` a value which didn't exist.
    #[test]
    #[should_panic]
    fn test_add_badly() {
        assert_eq!(add(2.0, 2.0), 5.0);
    }

    /// A yet more sophisticated example: `#[should_panic]` with `expected`.
    ///
    /// As the Rust book comments:
    ///
    /// > ...it's hard to guarantee that the test didn't fail for an unexpected
    /// > reason...
    ///
    /// The `#[should_panic]` annotation has an `expected` attribute.
    #[test]
    #[should_panic(expected = "Crazed monkeys!")]
    fn test_will_panic() {
        panic!("Crazed monkeys!");
    }

    /// Benchmark our addition function.
    ///
    /// Note: it's trivial, so it's probably pretty quick (`0 ns/iter (+/- 0)`).
    /// The point is simply that it does what it says on the tin.
    #[bench]
    fn demonstrate_benchmarking(bencher: &mut Bencher) {
        bencher.iter(|| add(2.0, 2.0));
    }

    /// We can also have secondary functions used to help with testing.
    ///
    /// This particular function is *stupid*; the way to do this, of course, is
    /// just to get the Duration directly. The only reason to have it here is to
    /// show that (1) `support_function()` doesn't end up in the compiled
    /// library, which you can check by inspecting the binary; and (2) that it
    /// is available for use with the benchmarker below.
    fn support_function(ns: u32) -> Duration {
        Duration::new(0, ns)
    }

    /// Benchmark a function that sleeps for 1ms every time you call it.
    ///
    /// One of the things this highlights: we have a *tiny* duration (10 ns)...
    /// and the test takes much, *much* longer. (I'm going to discuss this with
    /// the Rust community, because I don't actually understand it yet!)
    #[bench]
    fn demonstrate_benchmarking_with_sleep(bencher: &mut Bencher) {
        let duration = support_function(10);
        bencher.iter(|| sleep(duration));
    }
}