mock_it/
mock.rs

1use crate::rule::Rule;
2use crate::validator::*;
3use crate::when::When;
4use std::sync::Arc;
5use std::sync::Mutex;
6
7#[derive(Debug)]
8pub struct Mock<I, O> {
9    name: String,
10    calls: Arc<Mutex<Vec<I>>>,
11    rules: Arc<Mutex<Vec<Rule<I, O>>>>,
12}
13
14impl<I, O> Clone for Mock<I, O> {
15    fn clone(&self) -> Mock<I, O> {
16        Mock {
17            name: self.name.clone(),
18            calls: self.calls.clone(),
19            rules: self.rules.clone(),
20        }
21    }
22}
23
24impl<I, O> Mock<I, O> {
25    pub fn new(name: String) -> Mock<I, O> {
26        Mock {
27            name,
28            calls: Arc::new(Mutex::new(Vec::new())),
29            rules: Arc::new(Mutex::new(Vec::new())),
30        }
31    }
32}
33
34impl<I, O: Clone> Mock<I, O> {
35    pub fn when(&self, input: I) -> When<I, O> {
36        When::new(input, self.rules.clone())
37    }
38}
39
40impl<I: PartialEq + std::fmt::Debug, O: Clone> Mock<I, O> {
41    pub fn called(&self, input: I) -> O {
42        let input_str = format!("{:?}", input);
43
44        // Get the when value for this input
45        let rules = self.rules.lock().unwrap();
46        let when_value = rules.iter().find(|value| value.input == input);
47
48        // Record this call
49        self.calls.lock().unwrap().push(input);
50
51        // Return the when value, or fail if there is no when value
52        match when_value {
53            Some(value) => value.output.clone(),
54            None => panic!(
55                "Mock \"{}\" called with unexpected input: {:?}, did you forget to configure your mock ?",
56                self.name, input_str
57            ),
58        }
59    }
60}
61
62impl<I, O: Clone> Mock<I, O> {
63    pub fn was_called_with(&self, input: I) -> Validator<I> {
64        Validator::new(self.calls.clone(), input)
65    }
66}
67
68#[cfg(test)]
69mod test {
70    use super::*;
71
72    trait ATrait {
73        fn int_to_string(&self, input: i64) -> String;
74    }
75
76    #[derive(Clone)]
77    struct MyMock {
78        int_to_string: Mock<i64, String>,
79    }
80
81    impl MyMock {
82        fn new() -> MyMock {
83            MyMock {
84                int_to_string: Mock::new("AMockName".to_string()),
85            }
86        }
87    }
88
89    impl ATrait for MyMock {
90        fn int_to_string(&self, input: i64) -> String {
91            self.int_to_string.called(input)
92        }
93    }
94
95    /// Given some rules, the mock should return the output in the rule when
96    /// called
97    #[test]
98    fn mock_output() {
99        let mock = MyMock::new();
100        mock.int_to_string.when(65).will_return(String::from("65"));
101        mock.int_to_string.when(63).will_return(String::from("63"));
102        mock.int_to_string.when(-1).will_return(String::from("-1"));
103        let a_trait = Box::new(mock);
104
105        assert_eq!("65", a_trait.int_to_string(65));
106        assert_eq!("63", a_trait.int_to_string(63));
107        assert_eq!("-1", a_trait.int_to_string(-1));
108    }
109
110    /// The mock records the input it was called with
111    #[test]
112    fn was_called_with() {
113        let mock = MyMock::new();
114        mock.int_to_string.when(65).will_return(String::from(""));
115        mock.int_to_string.when(63).will_return(String::from(""));
116        mock.int_to_string.when(-1).will_return(String::from(""));
117        let a_trait = Box::new(mock.clone());
118
119        a_trait.int_to_string(65);
120        a_trait.int_to_string(63);
121        a_trait.int_to_string(-1);
122
123        assert!(verify(mock.int_to_string.was_called_with(65)));
124        assert!(verify(mock.int_to_string.was_called_with(63)));
125        assert!(verify(mock.int_to_string.was_called_with(-1)));
126        assert!(!verify(mock.int_to_string.was_called_with(0)));
127    }
128
129    #[test]
130    fn given_mock_called_5_times_when_times_5_then_return_true_false_otherwise() {
131        let mock = MyMock::new();
132        mock.int_to_string.when(65).will_return(String::from("65"));
133        let a_trait = Box::new(mock.clone());
134
135        for _ in 0..5 {
136            a_trait.int_to_string(65);
137        }
138
139        assert_eq!(
140            true,
141            verify(mock.int_to_string.was_called_with(65).times(5))
142        );
143        assert_eq!(
144            false,
145            verify(mock.int_to_string.was_called_with(65).times(4))
146        );
147        assert_eq!(
148            false,
149            verify(mock.int_to_string.was_called_with(65).times(1))
150        );
151        assert_eq!(
152            false,
153            verify(mock.int_to_string.was_called_with(65).times(6))
154        );
155    }
156}