diff --git a/Cargo.lock b/Cargo.lock index e97d4749f503..e235c7f49908 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -468,6 +468,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.7" @@ -478,6 +484,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "dtoa" version = "1.0.9" @@ -499,6 +511,12 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "elasticlunr-rs" version = "3.0.2" @@ -575,6 +593,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -605,6 +632,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -1009,6 +1042,15 @@ dependencies = [ name = "iterators" version = "0.1.0" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -1236,6 +1278,33 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "mockall" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -1260,6 +1329,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "normpath" version = "1.1.1" @@ -1595,6 +1670,36 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "pretty_env_logger" version = "0.5.0" @@ -2182,9 +2287,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "testing" version = "0.1.0" + "mockall", [[package]] name = "thiserror" diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 36db5b6ce58a..18075b25d040 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -172,6 +172,7 @@ - [Test Modules](testing/unit-tests.md) - [Other Types of Tests](testing/other.md) - [Useful Crates](testing/useful-crates.md) + - [Mocking](testing/mocking.md) - [Compiler lints and Clippy](testing/lints.md) - [Exercise: Luhn Algorithm](testing/exercise.md) - [Solution](testing/solution.md) diff --git a/src/testing/Cargo.toml b/src/testing/Cargo.toml index 10005b572536..87cc67dc14af 100644 --- a/src/testing/Cargo.toml +++ b/src/testing/Cargo.toml @@ -4,7 +4,14 @@ version = "0.1.0" edition = "2021" publish = false +[[example]] +name = "mockall-example" +crate-type = ["staticlib"] +path = "mockall.rs" +test = true + [[bin]] name = "luhn" path = "exercise.rs" +mockall = "0.11.4" diff --git a/src/testing/mockall.rs b/src/testing/mockall.rs new file mode 100644 index 000000000000..7ca6bc976bb5 --- /dev/null +++ b/src/testing/mockall.rs @@ -0,0 +1,13 @@ +use std::time::Duration; + +#[mockall::automock] +pub trait Pet { + fn is_hungry(&self, since_last_meal: Duration) -> bool; +} + +#[test] +fn test_robot_pet() { + let mut mock_dog = MockPet::new(); + mock_dog.expect_is_hungry().return_const(true); + assert_eq!(mock_dog.is_hungry(Duration::from_secs(10)), true); +} diff --git a/src/testing/mocking.md b/src/testing/mocking.md new file mode 100644 index 000000000000..4e69056036c2 --- /dev/null +++ b/src/testing/mocking.md @@ -0,0 +1,58 @@ +--- +minutes: 10 +--- + +# Mocking + +If you want to do mocking, we recommend [Mockall]. You need to refactor your +code to use traits, which you can then quickly mock: + +```rust,ignore +{{#include mockall.rs}} +``` + +[Mockall]: https://docs.rs/mockall/ + +
+ +- Note that *mocking is controversial*: mocks allow you to completely isolate a + test from its dependencies. The immediate result is faster and more stable + test execution. On the other hand, the mocks can be configured wrongly and + return output different from what the real dependencies would do. + + If at all possible, it is recommended that you use the real dependencies. As + an example, many databases allow you to configure an in-memory backend. This + means that you get the correct behavior in your tests, plus they are fast and + will automatically clean up after themselves. + + Similarly, many web frameworks allow you to start an in-process server which + binds to a random port on `localhost`. Always prefer this over mocking away + the framework since it helps you test your code in the real environment. + +- Mockall is not part of the Rust Playground, so you need to run this example in + a local environment. Use `cargo add mockall` to quickly add Mockall to an + existing Cargo project. + +- Mockall has a lot more functionality. In particular, you can set up + expectations which depend on the arguments passed: + + ```rust,ignore + let mut mock_cat = MockPet::new(); + mock_cat + .expect_is_hungry() + .with(mockall::predicate::gt(Duration::from_secs(3 * 3600))) + .return_const(true); + mock_cat + .expect_is_hungry() + .return_const(true); + assert_eq!(mock_cat.is_hungry(Duration::from_secs(1 * 3600)), false); + assert_eq!(mock_cat.is_hungry(Duration::from_secs(5 * 3600)), true); + ``` + +- You can use `.times(n)` to limit the number of times a mock method can be + called to `n` --- the mock will automatically panic when dropped if this isn't + satisfied. + +- Mockall is available for use in AOSP. + +