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 let rules = self.rules.lock().unwrap();
46 let when_value = rules.iter().find(|value| value.input == input);
47
48 self.calls.lock().unwrap().push(input);
50
51 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 #[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 #[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}