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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
#![feature(fn_traits, unboxed_closures)]

//! Mocking framework for Rust (currently only nightly)
//!
//! ```
//! #[mockable]
//! mod hello_world {
//!     pub fn world() -> &'static str {
//!         "world"
//!     }
//!
//!     pub fn hello_world() -> String {
//!         format!("Hello {}!", world())
//!     }
//! }
//!
//! #[test]
//! fn mock_test() {
//!     hello_world::world.mock_safe(|| MockResult::Return("mocking"));
//!
//!     assert_eq!("Hello mocking!", hello_world::hello_world());
//! }
//! ```
//! # Introduction
//! This is a user guide showing Rust project set up for testing with mocks.
//!
//! It is highly recommended to **use mocks ONLY for test runs and NEVER in release builds!**
//! Mocktopus is not designed for high performance and will slow down code execution.
//!
//! Note: this guide shows set up of mocking for test builds only.
//! # Prerequisites
//! Add Mocktopus dev-dependency to project's `Cargo.toml`:
//!
//! ```
//! [dev-dependencies]
//! mocktopus = "0.7.0"
//! ```
//! Enable procedural macros in crate root:
//!
//! ```
//! #![cfg_attr(test, feature(proc_macro_hygiene))]
//! ```
//! Import Mocktopus (skip for Rust 2018):
//!
//! ```
//! #[cfg(test)]
//! extern crate mocktopus;
//! ```
//! # Making functions mockable
//! To make functions mockable they must be annotated with provided procedural macros.
//! See [documentation](https://docs.rs/mocktopus_macros) for all their possibilities and rules.
//!
//! To use these macros import them into namespace:
//!
//! ```
//! #[cfg(test)]
//! use mocktopus::macros::*;
//! ```
//! Annotate mockable code like standalone functions or impl blocks:
//!
//! ```
//! #[mockable]
//! fn my_fn() {}
//!
//! #[mockable]
//! impl Struct {
//!     fn my_method() {}
//! }
//! ```
//! It's NOT legal to annotate single funciton in impl block:
//!
//! ```
//! impl Struct {
//!     #[mockable] // WRONG, will break Mocktopus
//!     fn my_method() {}
//! }
//! ```
//! It is possible to annotate modules, which makes all their potentially mockable content mockable:
//!
//! ```
//! #[cfg_attr(test, mockable)]
//! mod my_module {
//!     fn my_fn() {}
//! }
//! ```
//! This does NOT work for modules in separate file:
//!
//! ```
//! #[cfg_attr(test, mockable)] // WRONG, has no effect
//! mod my_module;
//! ```
//! # Mocking
//! Import tools for mocking in test module:
//!
//! ```
//! #[cfg(test)]
//! mod tests {
//!     use mocktopus::mocking::*;
//! ```
//! Among others this imports trait `Mockable`.
//! It is implemented for all functions and provides an interface for setting up mocks:
//!
//! ```
//! #[test]
//! fn my_test() {
//!     my_function.mock_safe(|| MockResult::Return(1));
//!
//!     assert_eq!(1, my_function());
//! }
//! ```
//! It is also possible to mock struct methods, either from own impls, traits or trait defaults:
//!
//! ```
//! // Mocking method
//! MyStruct::my_method.mock_safe(|| MockResult::Return(1));
//! // Mocking trait method
//! MyStruct::my_trait_method.mock_safe(|| MockResult::Return(2));
//! // Mocking default trait method
//! MyStruct::my_trait_default_method.mock_safe(|| MockResult::Return(3));
//! ```
//! Mocking with `mock_safe` is simplest, but the `Mockable` trait has more,
//! see [documantation](mocking/trait.Mockable.html).
//!
//! ## Mocking range
//! Every mock works only in thread, in which it was set.
//! All Rust test runs are executed in independent threads, so mocks do not leak between them:
//!
//! ```
//! #[cfg_attr(test, mockable)]
//! fn common_fn() -> u32 {
//!     0
//! }
//!
//! #[test]
//! fn common_fn_test_1() {
//!     assert_eq!(0, common_fn());
//!
//!     common_fn.mock_safe(|| MockResult::Return(1));
//!
//!     assert_eq!(1, common_fn());
//! }
//!
//! #[test]
//! fn common_fn_test_2() {
//!     assert_eq!(0, common_fn());
//!
//!     common_fn.mock_safe(|| MockResult::Return(2));
//!
//!     assert_eq!(2, common_fn());
//! }
//! ```
//!
//! ## Mock closure
//! `mock_safe` has single argument: a closure, which takes same input as mocked function and returns a `MockResult`.
//! Whenever the mocked function is called, its inputs are passed to the closure:
//!
//! ```
//! #[cfg_attr(test, mockable)]
//! fn my_function_1(_: u32) {
//!     return
//! }
//!
//! #[test]
//! fn my_function_1_test() {
//!     my_function_1.mock_safe(|x| {
//!         assert_eq!(2, x);
//!         MockResult::Return(())
//!     });
//!
//!     my_function_1(2); // Passes
//!     my_function_1(3); // Panics
//! }
//! ```
//! If the closure returns `MockResult::Return`, the mocked function does not run.
//! It immediately returns with a value, which is passed inside `MockResult::Return`:
//!
//! ```
//! #[cfg_attr(test, mockable)]
//! fn my_function_2() -> u32 {
//!     unreachable!()
//! }
//!
//! #[test]
//! fn my_function_2_test() {
//!     my_function_2.mock_safe(|| MockResult::Return(3));
//!
//!     assert_eq!(3, my_function_2());
//! }
//! ```
//! If the closure returns `MockResult::Continue`, the mocked function runs normally, but with changed arguments.
//! The new arguments are returned from closure in tuple inside `MockResult::Continue`:
//!
//! ```
//! #[cfg_attr(test, mockable)]
//! fn my_function_3(x: u32, y: u32) -> u32 {
//!     x + y
//! }
//!
//! #[test]
//! fn my_function_3_test() {
//!     my_function_3.mock_safe(|x, y| MockResult::Continue((x, y + 1)));
//!
//!     assert_eq!(3, my_function_3(1, 1));
//! }
//! ```
//!
//! ## Mocking generics
//! When mocking generic functions, all its generics must be defined and only this variant will be affected:
//!
//! ```
//! #[cfg_attr(test, mockable)]
//! fn generic_fn<T: Display>(t: T) -> String {
//!     t.to_string()
//! }
//!
//! #[test]
//! fn generic_fn_test() {
//!     generic_fn::<u32>.mock_safe(|_| MockResult::Return("mocked".to_string()));
//!
//!     assert_eq!("1", generic_fn(1i32));
//!     assert_eq!("mocked", generic_fn(1u32));
//! }
//! ```
//! The only exception are lifetimes, they are ignored:
//!
//! ```
//! #[cfg_attr(test, mockable)]
//! fn lifetime_generic_fn<'a>(string: &'a String) -> &'a str {
//!     string.as_ref()
//! }
//!
//! #[test]
//! fn lifetime_generic_fn_test() {
//!     lifetime_generic_fn.mock_safe(|_| MockResult::Return("mocked"));
//!
//!     assert_eq!("mocked", lifetime_generic_fn(&"not mocked".to_string()));
//! }
//! ```
//! Same rules apply to methods and structures:
//!
//! ```
//! struct GenericStruct<'a, T: Display + 'a>(&'a T);
//!
//! #[cfg_attr(test, mockable)]
//! impl<'a, T: Display + 'a> GenericStruct<'a, T> {
//!     fn to_string(&self) -> String {
//!         self.0.to_string()
//!     }
//! }
//!
//! static VALUE: u32 = 1;
//!
//! #[test]
//! fn lifetime_generic_fn_test() {
//!     GenericStruct::<u32>::to_string.mock_safe(|_| MockResult::Return("mocked".to_string()));
//!
//!     assert_eq!("mocked", GenericStruct(&VALUE).to_string());
//!     assert_eq!("mocked", GenericStruct(&2u32).to_string());
//!     assert_eq!("2", GenericStruct(&2i32).to_string());
//! }
//! ```
//!
//! ## Mocking async
//! Mocking async functions is almost exactly the same as non-async:
//!
//! ```
//! #[cfg_attr(test, mockable)]
//! async fn sleep(ms: u64) {
//!     tokio::time::delay_for(std::time::Duration::from_millis(ms)).await;
//! }
//!
//! #[tokio::test]
//! async fn sleep_test() {
//!     sleep.mock_safe(|_| MockResult::Return(Box::pin(async move { () })));
//!
//!     sleep(10000).await;
//! }
//! ```
//!
//! # Mocking tricks
//! ## Returning reference to value created inside mock
//!
//! ```
//! #[mockable]
//! fn my_fn(my_string: &String) -> &String {
//!     my_string
//! }
//!
//! #[test]
//! fn my_fn_test() {
//!     my_fn.mock_safe(|_| MockResult::Return(Box::leak(Box::new("mocked".to_string()))));
//!
//!     assert_eq!("mocked", my_fn(&"not mocked 1"));
//!     assert_eq!("mocked", my_fn(&"not mocked 2"));
//! }
//! ```
//! The trick is to store referenced value in a `Box::new` and then prevent its deallocation with `Box::leak`.
//! This makes structure live forever and returns a `&'static mut` reference to it. The value is not freed until
//! process termination, so it's viable solution only for use in tests and only if structure doesn't block a lot of
//! resources like huge amounts of memory, open file handlers, sockets, etc.
//!
//! ## Returning value created outside of mock
//!
//! ```
//! #[mockable]
//! fn my_fn() -> String {
//!     "not mocked".to_string()
//! }
//!
//! #[test]
//! fn my_fn_test() {
//!     mock = Some("mocked".to_string());
//!     my_fn.mock_safe(move || MockResult::Return(mock.unwrap()));
//!
//!     assert_eq!("mocked", my_fn());
//!     // assert_eq!("mocked", my_fn()); // WILL PANIC!
//! }
//! ```
//! This makes function return predefined value on first call and panic on second one. It could return
//! `MockResult::Continue` instead of panicking to mock only first call.
//!
//! Returned values can be stored in a vector if mock should return different value on different calls:
//!
//! ```//!
//! #[test]
//! fn my_fn_test() {
//!     mut mock = vec!["mocked 1".to_string(), "mocked 2".to_string()];
//!     my_fn.mock_safe(move || MockResult::Return(mock.remove(0)));
//!
//!     assert_eq!("mocked 1", my_fn());
//!     assert_eq!("mocked 2", my_fn());
//!     // assert_eq!("mocked 3", my_fn()); // WILL PANIC!
//! }
//! ```
//! The vector can store `MockResult`s for more complex mocking.
#![doc(
    html_logo_url = "https://raw.githubusercontent.com/CodeSandwich/mocktopus/master/logo.png",
    html_favicon_url = "https://raw.githubusercontent.com/CodeSandwich/mocktopus/master/logo.png"
)]

extern crate mocktopus_macros;

/// For use in testing code: mocking tools
pub mod mocking;

/// For use in testing code: helper tools for writing tests using mocking
pub mod mocking_utils;

/// For use in tested code: tools making items mockable
pub mod macros {
    pub use mocktopus_macros::*;
}

mod mock_store;