virtual_io/
vio_fake_builder.rs

1use crate::{
2    helpers::{append_stdout, VioFakeMessage},
3    vio_fake::VioFake,
4};
5
6/// Create a new `FakeVio` to be used for testing.
7///
8/// The builder first lets you specify the expected IO messages, then lets you
9/// create the `FakeVio`.
10///
11/// ```
12/// use virtual_io::VirtualIo;
13///
14/// fn get_name_base(vio: &mut impl VirtualIo) -> String {
15///     vio.print("What is your name? ");
16///     let name = vio.read_line();
17///     vio.println(format!("Hello, {}!", name));
18///     name
19/// }
20///
21/// #[cfg(test)]
22/// mod test {
23///     use super::*;
24///     use virtual_io::VioFakeBuilder;
25///
26///     #[test]
27///     fn test_get_name() {
28///         // Create a fake version of vio that we can inject into the base
29///         // function.
30///         let mut vio = VioFakeBuilder::new()
31///             // Add the expected io calls.
32///             .expect_stdout("What is your name? ")
33///             .provide_stdin("John")
34///             .expect_stdout("Hello, John!\n")
35///             // Build the fake vio.
36///             .build();
37///         // Assert that the return value is correct.
38///         assert_eq!(get_name_base(&mut vio), "John");
39///         // Assert that all io operations were identical to what was expected.
40///         assert_eq!(vio.get_actual(), vio.get_expected());
41///     }
42/// }
43/// ```
44#[derive(PartialEq, Eq, Debug, Default)]
45pub struct VioFakeBuilder {
46    messages: Vec<VioFakeMessage>,
47    environment_variables: Vec<(String, String)>,
48}
49
50impl VioFakeBuilder {
51    #[must_use]
52    pub const fn new() -> Self {
53        Self {
54            messages: Vec::new(),
55            environment_variables: Vec::new(),
56        }
57    }
58
59    pub fn expect_stdout<S: Into<String>>(&mut self, message: S) -> &mut Self {
60        append_stdout(&mut self.messages, message.into());
61        self
62    }
63
64    pub fn provide_stdin<S: Into<String>>(&mut self, message: S) -> &mut Self {
65        self.messages.push(VioFakeMessage::StdIn(message.into()));
66        self
67    }
68
69    pub fn set_environment_var<S: Into<String>>(&mut self, name: S, value: S) -> &mut Self {
70        let name = name.into();
71        let value = value.into();
72        for var in &mut self.environment_variables {
73            if var.0 == name {
74                var.1 = value;
75                return self;
76            }
77        }
78        self.environment_variables.push((name, value));
79        self
80    }
81
82    #[must_use]
83    pub fn build(&self) -> VioFake {
84        VioFake::new(self.messages.clone(), self.environment_variables.clone())
85    }
86}
87
88#[cfg(test)]
89mod test {
90    use super::*;
91
92    #[test]
93    fn vio_fake_builder_accepts_input() {
94        assert_eq!(
95            VioFakeBuilder::new()
96                .provide_stdin("John")
97                .build()
98                .expected_messages,
99            vec![VioFakeMessage::StdIn("John".to_string())]
100        );
101    }
102
103    #[test]
104    fn vio_fake_builder_inputs_do_not_collapse() {
105        assert_eq!(
106            VioFakeBuilder::new()
107                .provide_stdin("John")
108                .provide_stdin("Smith")
109                .build()
110                .expected_messages,
111            vec![
112                VioFakeMessage::StdIn("John".to_string()),
113                VioFakeMessage::StdIn("Smith".to_string())
114            ]
115        );
116    }
117
118    #[test]
119    fn vio_fake_builder_prints() {
120        assert_eq!(
121            VioFakeBuilder::new()
122                .expect_stdout("John")
123                .build()
124                .expected_messages,
125            vec![VioFakeMessage::StdOut("John".to_string())]
126        );
127    }
128
129    #[test]
130    fn vio_fake_builder_consecutive_print_statements_collapse_into_single_message() {
131        assert_eq!(
132            VioFakeBuilder::new()
133                .expect_stdout("I'm")
134                .expect_stdout(" ")
135                .expect_stdout("John")
136                .expect_stdout(' ')
137                .expect_stdout("Smith\n")
138                .expect_stdout("> What's your name? ")
139                .provide_stdin("Jane Doe")
140                .expect_stdout("Your name is Jane Doe.\n")
141                .expect_stdout("Nice to meet you Jane Doe.\n")
142                .build()
143                .expected_messages,
144            vec![
145                VioFakeMessage::StdOut("I'm John Smith\n> What's your name? ".to_string()),
146                VioFakeMessage::StdIn("Jane Doe".to_string()),
147                VioFakeMessage::StdOut(
148                    "Your name is Jane Doe.\nNice to meet you Jane Doe.\n".to_string()
149                )
150            ]
151        );
152    }
153
154    #[test]
155    fn vio_fake_builder_environment_variables() {
156        assert_eq!(
157            VioFakeBuilder::new()
158                .set_environment_var("HELLO", "WORLD")
159                .set_environment_var("MY_MOOD", "GOOD")
160                // overwrite the value of an existing environment variable
161                .set_environment_var("MY_MOOD", "SO_SO")
162                .build()
163                .environment_variables,
164            vec![
165                ("HELLO".to_string(), "WORLD".to_string()),
166                ("MY_MOOD".to_string(), "SO_SO".to_string()),
167            ]
168        );
169    }
170}