diff --git a/inquire/src/prompts/dateselect/test.rs b/inquire/src/prompts/dateselect/test.rs
index 4ec6ef59..76d1d15c 100644
--- a/inquire/src/prompts/dateselect/test.rs
+++ b/inquire/src/prompts/dateselect/test.rs
@@ -1,12 +1,11 @@
 use crate::{
     date_utils::get_current_date,
-    terminal::crossterm::CrosstermTerminal,
-    ui::{Backend, RenderConfig},
+    test::fake_backend,
+    ui::{Key, KeyModifiers},
     validator::Validation,
     DateSelect,
 };
 use chrono::NaiveDate;
-use crossterm::event::{KeyCode, KeyEvent};
 
 fn default<'a>() -> DateSelect<'a> {
     DateSelect::new("Question?")
@@ -20,12 +19,7 @@ macro_rules! date_test {
     ($name:ident,$input:expr,$output:expr,$prompt:expr) => {
         #[test]
         fn $name() {
-            let read: Vec<KeyEvent> = $input.into_iter().map(KeyEvent::from).collect();
-            let mut read = read.iter();
-
-            let mut write: Vec<u8> = Vec::new();
-            let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-            let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
+            let mut backend = fake_backend($input);
 
             let ans = $prompt.prompt_with_backend(&mut backend).unwrap();
 
@@ -34,11 +28,11 @@ macro_rules! date_test {
     };
 }
 
-date_test!(today_date, vec![KeyCode::Enter], get_current_date());
+date_test!(today_date, vec![Key::Enter], get_current_date());
 
 date_test!(
     custom_default_date,
-    vec![KeyCode::Enter],
+    vec![Key::Enter],
     NaiveDate::from_ymd_opt(2021, 1, 9).unwrap(),
     DateSelect::new("Date").with_default(NaiveDate::from_ymd_opt(2021, 1, 9).unwrap())
 );
@@ -47,11 +41,7 @@ date_test!(
 /// Tests that a closure that actually closes on a variable can be used
 /// as a DateSelect validator.
 fn closure_validator() {
-    let read: Vec<KeyEvent> = vec![KeyCode::Enter, KeyCode::Left, KeyCode::Enter]
-        .into_iter()
-        .map(KeyEvent::from)
-        .collect();
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![Key::Enter, Key::Left(KeyModifiers::NONE), Key::Enter]);
 
     let today_date = get_current_date();
 
@@ -63,10 +53,6 @@ fn closure_validator() {
         }
     };
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = DateSelect::new("Question")
         .with_validator(validator)
         .prompt_with_backend(&mut backend)
diff --git a/inquire/src/prompts/mod.rs b/inquire/src/prompts/mod.rs
index 9c6b600c..1475629a 100644
--- a/inquire/src/prompts/mod.rs
+++ b/inquire/src/prompts/mod.rs
@@ -10,6 +10,8 @@ mod one_liners;
 mod password;
 mod prompt;
 mod select;
+#[cfg(test)]
+pub(crate) mod test;
 mod text;
 
 pub use action::*;
diff --git a/inquire/src/prompts/multiselect/test.rs b/inquire/src/prompts/multiselect/test.rs
index 6a60800e..8c5bd098 100644
--- a/inquire/src/prompts/multiselect/test.rs
+++ b/inquire/src/prompts/multiselect/test.rs
@@ -1,31 +1,22 @@
 use crate::{
     formatter::MultiOptionFormatter,
     list_option::ListOption,
-    terminal::crossterm::CrosstermTerminal,
-    ui::{Backend, RenderConfig},
+    test::fake_backend,
+    ui::{Key, KeyModifiers},
     MultiSelect,
 };
-use crossterm::event::{KeyCode, KeyEvent};
 
 #[test]
 /// Tests that a closure that actually closes on a variable can be used
 /// as a Select formatter.
 fn closure_formatter() {
-    let read: Vec<KeyEvent> = vec![KeyCode::Char(' '), KeyCode::Enter]
-        .into_iter()
-        .map(KeyEvent::from)
-        .collect();
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![Key::Char(' ', KeyModifiers::NONE), Key::Enter]);
 
     let formatted = String::from("Thanks!");
     let formatter: MultiOptionFormatter<'_, i32> = &|_| formatted.clone();
 
     let options = vec![1, 2, 3];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = MultiSelect::new("Question", options)
         .with_formatter(formatter)
         .prompt_with_backend(&mut backend)
@@ -37,27 +28,18 @@ fn closure_formatter() {
 #[test]
 // Anti-regression test: https://github.com/mikaelmello/inquire/issues/30
 fn down_arrow_on_empty_list_does_not_panic() {
-    let read: Vec<KeyEvent> = [
-        KeyCode::Char('9'),
-        KeyCode::Down,
-        KeyCode::Backspace,
-        KeyCode::Char('3'),
-        KeyCode::Down,
-        KeyCode::Backspace,
-        KeyCode::Enter,
-    ]
-    .iter()
-    .map(|c| KeyEvent::from(*c))
-    .collect();
-
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![
+        Key::Char('9', KeyModifiers::NONE),
+        Key::Down(KeyModifiers::NONE),
+        Key::Backspace,
+        Key::Char('3', KeyModifiers::NONE),
+        Key::Down(KeyModifiers::NONE),
+        Key::Backspace,
+        Key::Enter,
+    ]);
 
     let options = vec![1, 2, 3];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = MultiSelect::new("Question", options)
         .prompt_with_backend(&mut backend)
         .unwrap();
@@ -67,19 +49,9 @@ fn down_arrow_on_empty_list_does_not_panic() {
 
 #[test]
 fn selecting_all_by_default_behavior() {
-    let read: Vec<KeyEvent> = [KeyCode::Enter, KeyCode::Enter]
-        .iter()
-        .map(|c| KeyEvent::from(*c))
-        .collect();
-
-    let mut read = read.iter();
-
+    let mut backend = fake_backend(vec![Key::Enter, Key::Enter]);
     let options = vec![1, 2, 3];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let answer_with_all_selected_by_default = MultiSelect::new("Question", options.clone())
         .with_all_selected_by_default()
         .prompt_with_backend(&mut backend)
@@ -105,24 +77,16 @@ fn selecting_all_by_default_behavior() {
 #[test]
 // Anti-regression test: https://github.com/mikaelmello/inquire/issues/31
 fn list_option_indexes_are_relative_to_input_vec() {
-    let read: Vec<KeyEvent> = vec![
-        KeyCode::Down,
-        KeyCode::Char(' '),
-        KeyCode::Down,
-        KeyCode::Char(' '),
-        KeyCode::Enter,
-    ]
-    .into_iter()
-    .map(KeyEvent::from)
-    .collect();
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![
+        Key::Down(KeyModifiers::NONE),
+        Key::Char(' ', KeyModifiers::NONE),
+        Key::Down(KeyModifiers::NONE),
+        Key::Char(' ', KeyModifiers::NONE),
+        Key::Enter,
+    ]);
 
     let options = vec![1, 2, 3];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = MultiSelect::new("Question", options)
         .prompt_with_backend(&mut backend)
         .unwrap();
@@ -133,19 +97,9 @@ fn list_option_indexes_are_relative_to_input_vec() {
 #[test]
 // Anti-regression test: https://github.com/mikaelmello/inquire/issues/195
 fn starting_cursor_is_respected() {
-    let read: Vec<KeyEvent> = [KeyCode::Char(' '), KeyCode::Enter]
-        .iter()
-        .map(|c| KeyEvent::from(*c))
-        .collect();
-
-    let mut read = read.iter();
-
+    let mut backend = fake_backend(vec![Key::Char(' ', KeyModifiers::NONE), Key::Enter]);
     let options = vec![1, 2, 3];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = MultiSelect::new("Question", options)
         .with_starting_cursor(2)
         .prompt_with_backend(&mut backend)
@@ -156,19 +110,14 @@ fn starting_cursor_is_respected() {
 
 #[test]
 fn naive_assert_fuzzy_match_as_default_scorer() {
-    let read: Vec<KeyEvent> = [
-        KeyCode::Char('w'),
-        KeyCode::Char('r'),
-        KeyCode::Char('r'),
-        KeyCode::Char('y'),
-        KeyCode::Char(' '),
-        KeyCode::Enter,
-    ]
-    .iter()
-    .map(|c| KeyEvent::from(*c))
-    .collect();
-
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![
+        Key::Char('w', KeyModifiers::NONE),
+        Key::Char('r', KeyModifiers::NONE),
+        Key::Char('r', KeyModifiers::NONE),
+        Key::Char('y', KeyModifiers::NONE),
+        Key::Char(' ', KeyModifiers::NONE),
+        Key::Enter,
+    ]);
 
     let options = vec![
         "Banana",
@@ -184,10 +133,6 @@ fn naive_assert_fuzzy_match_as_default_scorer() {
         "Pineapple",
     ];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = MultiSelect::new("Question", options)
         .prompt_with_backend(&mut backend)
         .unwrap();
@@ -197,19 +142,14 @@ fn naive_assert_fuzzy_match_as_default_scorer() {
 
 #[test]
 fn chars_do_not_affect_prompt_without_filtering() {
-    let read: Vec<KeyEvent> = [
-        KeyCode::Char('w'),
-        KeyCode::Char('r'),
-        KeyCode::Char('r'),
-        KeyCode::Char('y'),
-        KeyCode::Char(' '),
-        KeyCode::Enter,
-    ]
-    .iter()
-    .map(|c| KeyEvent::from(*c))
-    .collect();
-
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![
+        Key::Char('w', KeyModifiers::NONE),
+        Key::Char('r', KeyModifiers::NONE),
+        Key::Char('r', KeyModifiers::NONE),
+        Key::Char('y', KeyModifiers::NONE),
+        Key::Char(' ', KeyModifiers::NONE),
+        Key::Enter,
+    ]);
 
     let options = vec![
         "Banana",
@@ -225,10 +165,6 @@ fn chars_do_not_affect_prompt_without_filtering() {
         "Pineapple",
     ];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = MultiSelect::new("Question", options)
         .without_filtering()
         .prompt_with_backend(&mut backend)
diff --git a/inquire/src/prompts/password/test.rs b/inquire/src/prompts/password/test.rs
index 494d2abc..cfad798a 100644
--- a/inquire/src/prompts/password/test.rs
+++ b/inquire/src/prompts/password/test.rs
@@ -1,14 +1,13 @@
 use super::Password;
-use crate::{
-    terminal::crossterm::CrosstermTerminal,
-    ui::{Backend, RenderConfig},
-    validator::{ErrorMessage, Validation},
-};
-use crossterm::event::{KeyCode, KeyEvent};
+use crate::ui::{Key, KeyModifiers};
+use crate::validator::{ErrorMessage, Validation};
 
 macro_rules! text_to_events {
     ($text:expr) => {{
-        $text.chars().map(KeyCode::Char)
+        $text
+            .chars()
+            .map(|c| Key::Char(c, KeyModifiers::NONE))
+            .collect()
     }};
 }
 
@@ -17,12 +16,7 @@ macro_rules! password_test {
         #[test]
         $(#[$meta])?
         fn $name() {
-            let read: Vec<KeyEvent> = $input.into_iter().map(KeyEvent::from).collect();
-            let mut read = read.iter();
-
-            let mut write: Vec<u8> = Vec::new();
-            let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-            let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
+            let mut backend = crate::prompts::test::fake_backend($input);
 
             let ans = $prompt.prompt_with_backend(&mut backend).unwrap();
 
@@ -33,14 +27,14 @@ macro_rules! password_test {
 
 password_test!(
     empty,
-    vec![KeyCode::Enter],
+    vec![Key::Enter],
     "",
     Password::new("").without_confirmation()
 );
 
 password_test!(
     single_letter,
-    vec![KeyCode::Char('b'), KeyCode::Enter],
+    vec![Key::Char('b', KeyModifiers::NONE), Key::Enter],
     "b",
     Password::new("").without_confirmation()
 );
@@ -63,13 +57,13 @@ password_test!(
     input_and_correction,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.append(&mut text_to_events!("normal input").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.append(&mut text_to_events!("normal input"));
+        events.push(Key::Enter);
         events
     },
     "normal input",
@@ -80,19 +74,19 @@ password_test!(
     input_and_excessive_correction,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.append(&mut text_to_events!("normal input").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.append(&mut text_to_events!("normal input"));
+        events.push(Key::Enter);
         events
     },
     "normal input",
@@ -103,15 +97,15 @@ password_test!(
     input_correction_after_validation_when_masked,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("1234567890").collect());
-        events.push(KeyCode::Enter);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.append(&mut text_to_events!("yes").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("1234567890"));
+        events.push(Key::Enter);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.append(&mut text_to_events!("yes"));
+        events.push(Key::Enter);
         events
     },
     "12345yes",
@@ -128,15 +122,15 @@ password_test!(
     input_correction_after_validation_when_full,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("1234567890").collect());
-        events.push(KeyCode::Enter);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.append(&mut text_to_events!("yes").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("1234567890"));
+        events.push(Key::Enter);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.append(&mut text_to_events!("yes"));
+        events.push(Key::Enter);
         events
     },
     "12345yes",
@@ -153,10 +147,10 @@ password_test!(
     input_correction_after_validation_when_hidden,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("1234567890").collect());
-        events.push(KeyCode::Enter);
-        events.append(&mut text_to_events!("yesyes").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("1234567890"));
+        events.push(Key::Enter);
+        events.append(&mut text_to_events!("yesyes"));
+        events.push(Key::Enter);
         events
     },
     "yesyes",
@@ -173,10 +167,10 @@ password_test!(
     input_confirmation_same,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("1234567890").collect());
-        events.push(KeyCode::Enter);
-        events.append(&mut text_to_events!("1234567890").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("1234567890"));
+        events.push(Key::Enter);
+        events.append(&mut text_to_events!("1234567890"));
+        events.push(Key::Enter);
         events
     },
     "1234567890",
@@ -188,10 +182,10 @@ password_test!(
     input_confirmation_different,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("1234567890").collect());
-        events.push(KeyCode::Enter);
-        events.append(&mut text_to_events!("abcdefghij").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("1234567890"));
+        events.push(Key::Enter);
+        events.append(&mut text_to_events!("abcdefghij"));
+        events.push(Key::Enter);
         events
     },
     "",
@@ -203,16 +197,16 @@ password_test!(
     prompt_with_hidden_should_clear_on_mismatch,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Enter);
-        events.append(&mut text_to_events!("anor2").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Enter);
+        events.append(&mut text_to_events!("anor2"));
+        events.push(Key::Enter);
         // The problem is that the 1st input values were not cleared
         // and the lack of a change in the 1st prompt can be confusing.
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Enter);
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Enter);
         events
     },
     "anor",
@@ -224,16 +218,16 @@ password_test!(
     prompt_with_full_should_clear_1st_on_mismatch,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Enter);
-        events.append(&mut text_to_events!("anor2").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Enter);
+        events.append(&mut text_to_events!("anor2"));
+        events.push(Key::Enter);
         // The problem is that the 1st input values were not cleared
         // and the lack of a change in the 1st prompt can be confusing.
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Enter);
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Enter);
         events
     },
     "anor",
@@ -245,16 +239,16 @@ password_test!(
     prompt_with_masked_should_clear_1st_on_mismatch,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Enter);
-        events.append(&mut text_to_events!("anor2").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Enter);
+        events.append(&mut text_to_events!("anor2"));
+        events.push(Key::Enter);
         // The problem is that the 1st input values were not cleared
         // and the lack of a change in the 1st prompt can be confusing.
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Enter);
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Enter);
         events
     },
     "anor",
diff --git a/inquire/src/prompts/select/test.rs b/inquire/src/prompts/select/test.rs
index c3d2b29e..8b00e387 100644
--- a/inquire/src/prompts/select/test.rs
+++ b/inquire/src/prompts/select/test.rs
@@ -1,31 +1,20 @@
 use crate::{
     formatter::OptionFormatter,
     list_option::ListOption,
-    terminal::crossterm::CrosstermTerminal,
-    ui::{Backend, RenderConfig},
+    test::fake_backend,
+    ui::{Key, KeyModifiers},
     Select,
 };
-use crossterm::event::{KeyCode, KeyEvent};
 
 #[test]
 /// Tests that a closure that actually closes on a variable can be used
 /// as a Select formatter.
 fn closure_formatter() {
-    let read: Vec<KeyEvent> = vec![KeyCode::Down, KeyCode::Enter]
-        .into_iter()
-        .map(KeyEvent::from)
-        .collect();
-    let mut read = read.iter();
-
-    let formatted = String::from("Thanks!");
-    let formatter: OptionFormatter<'_, i32> = &|_| formatted.clone();
+    let mut backend = fake_backend(vec![Key::Down(KeyModifiers::NONE), Key::Enter]);
 
+    let formatter: OptionFormatter<'_, i32> = &|_| String::from("Thanks!");
     let options = vec![1, 2, 3];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = Select::new("Question", options)
         .with_formatter(formatter)
         .prompt_with_backend(&mut backend)
@@ -37,25 +26,16 @@ fn closure_formatter() {
 #[test]
 // Anti-regression test: https://github.com/mikaelmello/inquire/issues/29
 fn enter_arrow_on_empty_list_does_not_panic() {
-    let read: Vec<KeyEvent> = [
-        KeyCode::Char('9'),
-        KeyCode::Enter,
-        KeyCode::Backspace,
-        KeyCode::Char('3'),
-        KeyCode::Enter,
-    ]
-    .iter()
-    .map(|c| KeyEvent::from(*c))
-    .collect();
-
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![
+        Key::Char('9', KeyModifiers::NONE),
+        Key::Enter,
+        Key::Backspace,
+        Key::Char('3', KeyModifiers::NONE),
+        Key::Enter,
+    ]);
 
     let options = vec![1, 2, 3];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = Select::new("Question", options)
         .prompt_with_backend(&mut backend)
         .unwrap();
@@ -66,27 +46,18 @@ fn enter_arrow_on_empty_list_does_not_panic() {
 #[test]
 // Anti-regression test: https://github.com/mikaelmello/inquire/issues/30
 fn down_arrow_on_empty_list_does_not_panic() {
-    let read: Vec<KeyEvent> = [
-        KeyCode::Char('9'),
-        KeyCode::Down,
-        KeyCode::Backspace,
-        KeyCode::Char('3'),
-        KeyCode::Down,
-        KeyCode::Backspace,
-        KeyCode::Enter,
-    ]
-    .iter()
-    .map(|c| KeyEvent::from(*c))
-    .collect();
-
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![
+        Key::Char('9', KeyModifiers::NONE),
+        Key::Down(KeyModifiers::NONE),
+        Key::Backspace,
+        Key::Char('3', KeyModifiers::NONE),
+        Key::Down(KeyModifiers::NONE),
+        Key::Backspace,
+        Key::Enter,
+    ]);
 
     let options = vec![1, 2, 3];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = Select::new("Question", options)
         .prompt_with_backend(&mut backend)
         .unwrap();
@@ -97,19 +68,10 @@ fn down_arrow_on_empty_list_does_not_panic() {
 #[test]
 // Anti-regression test: https://github.com/mikaelmello/inquire/issues/195
 fn starting_cursor_is_respected() {
-    let read: Vec<KeyEvent> = [KeyCode::Enter]
-        .iter()
-        .map(|c| KeyEvent::from(*c))
-        .collect();
-
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![Key::Enter]);
 
     let options = vec![1, 2, 3];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = Select::new("Question", options)
         .with_starting_cursor(2)
         .prompt_with_backend(&mut backend)
@@ -120,18 +82,13 @@ fn starting_cursor_is_respected() {
 
 #[test]
 fn naive_assert_fuzzy_match_as_default_scorer() {
-    let read: Vec<KeyEvent> = [
-        KeyCode::Char('w'),
-        KeyCode::Char('r'),
-        KeyCode::Char('r'),
-        KeyCode::Char('y'),
-        KeyCode::Enter,
-    ]
-    .iter()
-    .map(|c| KeyEvent::from(*c))
-    .collect();
-
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![
+        Key::Char('w', KeyModifiers::NONE),
+        Key::Char('r', KeyModifiers::NONE),
+        Key::Char('r', KeyModifiers::NONE),
+        Key::Char('y', KeyModifiers::NONE),
+        Key::Enter,
+    ]);
 
     let options = vec![
         "Banana",
@@ -147,10 +104,6 @@ fn naive_assert_fuzzy_match_as_default_scorer() {
         "Pineapple",
     ];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = Select::new("Question", options)
         .prompt_with_backend(&mut backend)
         .unwrap();
@@ -160,18 +113,13 @@ fn naive_assert_fuzzy_match_as_default_scorer() {
 
 #[test]
 fn chars_do_not_affect_prompt_without_filtering() {
-    let read: Vec<KeyEvent> = [
-        KeyCode::Char('w'),
-        KeyCode::Char('r'),
-        KeyCode::Char('r'),
-        KeyCode::Char('y'),
-        KeyCode::Enter,
-    ]
-    .iter()
-    .map(|c| KeyEvent::from(*c))
-    .collect();
-
-    let mut read = read.iter();
+    let mut backend = fake_backend(vec![
+        Key::Char('w', KeyModifiers::NONE),
+        Key::Char('r', KeyModifiers::NONE),
+        Key::Char('r', KeyModifiers::NONE),
+        Key::Char('y', KeyModifiers::NONE),
+        Key::Enter,
+    ]);
 
     let options = vec![
         "Banana",
@@ -187,10 +135,6 @@ fn chars_do_not_affect_prompt_without_filtering() {
         "Pineapple",
     ];
 
-    let mut write: Vec<u8> = Vec::new();
-    let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-    let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
-
     let ans = Select::new("Question", options)
         .without_filtering()
         .prompt_with_backend(&mut backend)
diff --git a/inquire/src/prompts/test.rs b/inquire/src/prompts/test.rs
new file mode 100644
index 00000000..2f9f629c
--- /dev/null
+++ b/inquire/src/prompts/test.rs
@@ -0,0 +1,18 @@
+use std::convert::TryFrom;
+
+use crossterm::event::KeyEvent;
+
+use crate::{
+    terminal::crossterm::CrosstermTerminal,
+    ui::{Backend, Key, RenderConfig},
+};
+
+pub fn fake_backend(input: Vec<Key>) -> Backend<'static, CrosstermTerminal> {
+    let events: Vec<KeyEvent> = input
+        .into_iter()
+        .map(|k| KeyEvent::try_from(k).expect("Could not convert Key to KeyEvent"))
+        .collect();
+    let terminal = CrosstermTerminal::new_with_io(events.into());
+
+    Backend::new(terminal, RenderConfig::default()).unwrap()
+}
diff --git a/inquire/src/prompts/text/test.rs b/inquire/src/prompts/text/test.rs
index 94f0099e..35d43e58 100644
--- a/inquire/src/prompts/text/test.rs
+++ b/inquire/src/prompts/text/test.rs
@@ -1,10 +1,6 @@
 use super::Text;
-use crate::{
-    terminal::crossterm::CrosstermTerminal,
-    ui::{Backend, RenderConfig},
-    validator::{ErrorMessage, Validation},
-};
-use crossterm::event::{KeyCode, KeyEvent};
+use crate::ui::{Key, KeyModifiers};
+use crate::validator::{ErrorMessage, Validation};
 
 fn default<'a>() -> Text<'a> {
     Text::new("Question?")
@@ -12,7 +8,10 @@ fn default<'a>() -> Text<'a> {
 
 macro_rules! text_to_events {
     ($text:expr) => {{
-        $text.chars().map(KeyCode::Char)
+        $text
+            .chars()
+            .map(|c| Key::Char(c, KeyModifiers::NONE))
+            .collect::<Vec<Key>>()
     }};
 }
 
@@ -24,13 +23,7 @@ macro_rules! text_test {
     ($name:ident,$input:expr,$output:expr,$prompt:expr) => {
         #[test]
         fn $name() {
-            let read: Vec<KeyEvent> = $input.into_iter().map(KeyEvent::from).collect();
-            let mut read = read.iter();
-
-            let mut write: Vec<u8> = Vec::new();
-
-            let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-            let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();
+            let mut backend = crate::prompts::test::fake_backend($input);
 
             let ans = $prompt.prompt_with_backend(&mut backend).unwrap();
 
@@ -39,9 +32,13 @@ macro_rules! text_test {
     };
 }
 
-text_test!(empty, vec![KeyCode::Enter], "");
+text_test!(empty, vec![Key::Enter], "");
 
-text_test!(single_letter, vec![KeyCode::Char('b'), KeyCode::Enter], "b");
+text_test!(
+    single_letter,
+    vec![Key::Char('b', KeyModifiers::NONE), Key::Enter],
+    "b"
+);
 
 text_test!(
     letters_and_enter,
@@ -59,13 +56,13 @@ text_test!(
     input_and_correction,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.append(&mut text_to_events!("normal input").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.append(&mut text_to_events!("normal input"));
+        events.push(Key::Enter);
         events
     },
     "normal input"
@@ -75,19 +72,19 @@ text_test!(
     input_and_excessive_correction,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("anor").collect());
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.append(&mut text_to_events!("normal input").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("anor"));
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.append(&mut text_to_events!("normal input"));
+        events.push(Key::Enter);
         events
     },
     "normal input"
@@ -97,15 +94,15 @@ text_test!(
     input_correction_after_validation,
     {
         let mut events = vec![];
-        events.append(&mut text_to_events!("1234567890").collect());
-        events.push(KeyCode::Enter);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.push(KeyCode::Backspace);
-        events.append(&mut text_to_events!("yes").collect());
-        events.push(KeyCode::Enter);
+        events.append(&mut text_to_events!("1234567890"));
+        events.push(Key::Enter);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.push(Key::Backspace);
+        events.append(&mut text_to_events!("yes"));
+        events.push(Key::Enter);
         events
     },
     "12345yes",
diff --git a/inquire/src/terminal/crossterm.rs b/inquire/src/terminal/crossterm.rs
index d0adcdf9..555c7102 100644
--- a/inquire/src/terminal/crossterm.rs
+++ b/inquire/src/terminal/crossterm.rs
@@ -1,4 +1,7 @@
-use std::io::{stderr, Result, Stderr, Write};
+use std::{
+    collections::VecDeque,
+    io::{stderr, Result, Stderr, Write},
+};
 
 use crossterm::{
     cursor,
@@ -16,23 +19,23 @@ use crate::{
 
 use super::{Terminal, INITIAL_IN_MEMORY_CAPACITY};
 
-enum IO<'a> {
+enum IO {
     Std {
         w: Stderr,
     },
     #[allow(unused)]
-    Custom {
-        r: &'a mut dyn Iterator<Item = &'a KeyEvent>,
-        w: &'a mut (dyn Write),
+    Test {
+        w: Vec<u8>,
+        r: VecDeque<KeyEvent>,
     },
 }
 
-pub struct CrosstermTerminal<'a> {
-    io: IO<'a>,
+pub struct CrosstermTerminal {
+    io: IO,
     in_memory_content: String,
 }
 
-impl<'a> CrosstermTerminal<'a> {
+impl CrosstermTerminal {
     pub fn new() -> InquireResult<Self> {
         crossterm::terminal::enable_raw_mode()?;
 
@@ -42,27 +45,10 @@ impl<'a> CrosstermTerminal<'a> {
         })
     }
 
-    /// # Errors
-    ///
-    /// Will return `std::io::Error` if it fails to get terminal size
-    #[cfg(test)]
-    pub fn new_with_io<W: 'a + Write>(
-        writer: &'a mut W,
-        reader: &'a mut dyn Iterator<Item = &'a KeyEvent>,
-    ) -> Self {
-        Self {
-            io: IO::Custom {
-                r: reader,
-                w: writer,
-            },
-            in_memory_content: String::with_capacity(INITIAL_IN_MEMORY_CAPACITY),
-        }
-    }
-
     fn get_writer(&mut self) -> &mut dyn Write {
         match &mut self.io {
             IO::Std { w } => w,
-            IO::Custom { r: _, w } => w,
+            IO::Test { r: _, w } => w,
         }
     }
 
@@ -102,7 +88,7 @@ impl<'a> CrosstermTerminal<'a> {
     }
 }
 
-impl<'a> Terminal for CrosstermTerminal<'a> {
+impl Terminal for CrosstermTerminal {
     fn cursor_up(&mut self, cnt: u16) -> Result<()> {
         self.write_command(cursor::MoveUp(cnt))
     }
@@ -123,9 +109,11 @@ impl<'a> Terminal for CrosstermTerminal<'a> {
                         return Ok(key_event.into());
                     }
                 }
-                IO::Custom { r, w: _ } => {
-                    let key = r.next().expect("Custom stream of characters has ended");
-                    return Ok((*key).into());
+                IO::Test { r, w: _ } => {
+                    let key = r
+                        .pop_front()
+                        .expect("Custom stream of characters has ended");
+                    return Ok(key.into());
                 }
             }
         }
@@ -194,12 +182,12 @@ impl<'a> Terminal for CrosstermTerminal<'a> {
     }
 }
 
-impl<'a> Drop for CrosstermTerminal<'a> {
+impl Drop for CrosstermTerminal {
     fn drop(&mut self) {
         let _unused = self.flush();
         let _unused = match self.io {
             IO::Std { w: _ } => terminal::disable_raw_mode(),
-            IO::Custom { r: _, w: _ } => Ok(()),
+            IO::Test { r: _, w: _ } => Ok(()),
         };
     }
 }
@@ -322,116 +310,158 @@ impl From<KeyEvent> for Key {
 
 #[cfg(test)]
 mod test {
+    use std::collections::VecDeque;
+    use std::convert::TryFrom;
+    use std::convert::TryInto;
+
+    use crossterm::event::KeyCode;
+    use crossterm::event::KeyEvent;
+    use crossterm::event::KeyModifiers;
+
     use crate::terminal::Terminal;
+    use crate::terminal::INITIAL_IN_MEMORY_CAPACITY;
     use crate::ui::Color;
+    use crate::ui::Key;
 
     use super::Attributes;
     use super::CrosstermTerminal;
+    use super::IO;
 
-    #[test]
-    fn writer() {
-        let mut write: Vec<u8> = Vec::new();
-        let read = Vec::new();
-        let mut read = read.iter();
+    impl TryFrom<crate::ui::KeyModifiers> for KeyModifiers {
+        type Error = ();
 
-        {
-            let mut terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
+        fn try_from(value: crate::ui::KeyModifiers) -> Result<Self, Self::Error> {
+            Self::from_bits(value.bits()).ok_or(())
+        }
+    }
 
-            terminal.write("testing ").unwrap();
-            terminal.write("writing ").unwrap();
-            terminal.flush().unwrap();
-            terminal.write("wow").unwrap();
+    impl TryFrom<Key> for KeyEvent {
+        type Error = ();
+
+        fn try_from(value: Key) -> Result<Self, Self::Error> {
+            let key_event = match value {
+                Key::Escape => KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE),
+                Key::Enter => KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE),
+                Key::Backspace => KeyEvent::new(KeyCode::Backspace, KeyModifiers::NONE),
+                Key::Tab => KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE),
+                Key::Delete(m) => KeyEvent::new(KeyCode::Delete, m.try_into()?),
+                Key::Home => KeyEvent::new(KeyCode::Home, KeyModifiers::NONE),
+                Key::End => KeyEvent::new(KeyCode::End, KeyModifiers::NONE),
+                Key::PageUp => KeyEvent::new(KeyCode::PageUp, KeyModifiers::NONE),
+                Key::PageDown => KeyEvent::new(KeyCode::PageDown, KeyModifiers::NONE),
+                Key::Up(m) => KeyEvent::new(KeyCode::Up, m.try_into()?),
+                Key::Down(m) => KeyEvent::new(KeyCode::Down, m.try_into()?),
+                Key::Left(m) => KeyEvent::new(KeyCode::Left, m.try_into()?),
+                Key::Right(m) => KeyEvent::new(KeyCode::Right, m.try_into()?),
+                Key::Char(c, m) => KeyEvent::new(KeyCode::Char(c), m.try_into()?),
+                #[allow(deprecated)]
+                Key::Any => KeyEvent::new(KeyCode::Null, KeyModifiers::NONE),
+            };
+
+            Ok(key_event)
         }
+    }
+
+    impl CrosstermTerminal {
+        pub fn new_with_io(events: VecDeque<KeyEvent>) -> Self {
+            Self {
+                io: IO::Test {
+                    r: events,
+                    w: Vec::new(),
+                },
+                in_memory_content: String::with_capacity(INITIAL_IN_MEMORY_CAPACITY),
+            }
+        }
+
+        pub fn get_buffer_content(&mut self) -> Vec<u8> {
+            match &mut self.io {
+                IO::Std { w: _ } => panic!("Cannot get write buffer from standard output"),
+                IO::Test { r: _, w } => {
+                    let mut buffer = Vec::new();
+                    std::mem::swap(&mut buffer, w);
+                    buffer
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn writer() {
+        let mut terminal = CrosstermTerminal::new_with_io(VecDeque::new());
+
+        terminal.write("testing ").unwrap();
+        terminal.write("writing ").unwrap();
+        terminal.flush().unwrap();
+        terminal.write("wow").unwrap();
 
         #[cfg(unix)]
-        assert_eq!("testing writing wow", std::str::from_utf8(&write).unwrap());
+        assert_eq!(
+            "testing writing wow",
+            std::str::from_utf8(&terminal.get_buffer_content()).unwrap()
+        );
     }
 
     #[test]
     fn style_management() {
-        let mut write: Vec<u8> = Vec::new();
-        let read = Vec::new();
-        let mut read = read.iter();
+        let mut terminal = CrosstermTerminal::new_with_io(VecDeque::new());
 
-        {
-            let mut terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-
-            terminal.set_attributes(Attributes::BOLD).unwrap();
-            terminal.set_attributes(Attributes::ITALIC).unwrap();
-            terminal.set_attributes(Attributes::BOLD).unwrap();
-            terminal.reset_attributes().unwrap();
-        }
+        terminal.set_attributes(Attributes::BOLD).unwrap();
+        terminal.set_attributes(Attributes::ITALIC).unwrap();
+        terminal.set_attributes(Attributes::BOLD).unwrap();
+        terminal.reset_attributes().unwrap();
 
         #[cfg(unix)]
         assert_eq!(
             "\x1B[1m\x1B[3m\x1B[1m\x1B[0m",
-            std::str::from_utf8(&write).unwrap()
+            std::str::from_utf8(&terminal.get_buffer_content()).unwrap()
         );
     }
 
     #[test]
     fn style_management_with_flags() {
-        let mut write: Vec<u8> = Vec::new();
-        let read = Vec::new();
-        let mut read = read.iter();
+        let mut terminal = CrosstermTerminal::new_with_io(VecDeque::new());
 
-        {
-            let mut terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-
-            terminal
-                .set_attributes(Attributes::BOLD | Attributes::ITALIC | Attributes::BOLD)
-                .unwrap();
-            terminal.reset_attributes().unwrap();
-        }
+        terminal
+            .set_attributes(Attributes::BOLD | Attributes::ITALIC | Attributes::BOLD)
+            .unwrap();
+        terminal.reset_attributes().unwrap();
 
         #[cfg(unix)]
         assert_eq!(
             "\x1B[1m\x1B[3m\x1B[0m",
-            std::str::from_utf8(&write).unwrap()
+            std::str::from_utf8(&terminal.get_buffer_content()).unwrap()
         );
     }
 
     #[test]
     fn fg_color_management() {
-        let mut write: Vec<u8> = Vec::new();
-        let read = Vec::new();
-        let mut read = read.iter();
+        let mut terminal = CrosstermTerminal::new_with_io(VecDeque::new());
 
-        {
-            let mut terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-
-            terminal.set_fg_color(Color::LightRed).unwrap();
-            terminal.reset_fg_color().unwrap();
-            terminal.set_fg_color(Color::Black).unwrap();
-            terminal.set_fg_color(Color::LightGreen).unwrap();
-        }
+        terminal.set_fg_color(Color::LightRed).unwrap();
+        terminal.reset_fg_color().unwrap();
+        terminal.set_fg_color(Color::Black).unwrap();
+        terminal.set_fg_color(Color::LightGreen).unwrap();
 
         #[cfg(unix)]
         assert_eq!(
             "\x1B[38;5;9m\x1B[39m\x1B[38;5;0m\x1B[38;5;10m",
-            std::str::from_utf8(&write).unwrap()
+            std::str::from_utf8(&terminal.get_buffer_content()).unwrap()
         );
     }
 
     #[test]
     fn bg_color_management() {
-        let mut write: Vec<u8> = Vec::new();
-        let read = Vec::new();
-        let mut read = read.iter();
+        let mut terminal = CrosstermTerminal::new_with_io(VecDeque::new());
 
-        {
-            let mut terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
-
-            terminal.set_bg_color(Color::LightRed).unwrap();
-            terminal.reset_bg_color().unwrap();
-            terminal.set_bg_color(Color::Black).unwrap();
-            terminal.set_bg_color(Color::LightGreen).unwrap();
-        }
+        terminal.set_bg_color(Color::LightRed).unwrap();
+        terminal.reset_bg_color().unwrap();
+        terminal.set_bg_color(Color::Black).unwrap();
+        terminal.set_bg_color(Color::LightGreen).unwrap();
 
         #[cfg(unix)]
         assert_eq!(
             "\x1B[48;5;9m\x1B[49m\x1B[48;5;0m\x1B[48;5;10m",
-            std::str::from_utf8(&write).unwrap()
+            std::str::from_utf8(&terminal.get_buffer_content()).unwrap()
         );
     }
 }