我正在为我的第一个Rust项目编写一个CLI question asking library,因为无论如何我可能会使用它,而且我找不到一种干净的方法来测试构建器模式的terminal
方法,它使用了配置获取用户输入并返回答案。
pub fn confirm(&mut self) -> Answer {
self.yes_no();
self.build_prompt();
let prompt = self.prompt.clone();
let valid_responses = self.valid_responses.clone().unwrap();
loop {
let stdio = io::stdin();
let input = stdio.lock();
let output = io::stdout();
if let Ok(response) = prompt_user(input, output, &prompt) {
for key in valid_responses.keys() {
if *response.trim().to_lowercase() == *key {
return valid_responses.get(key).unwrap().clone();
}
}
self.build_clarification();
}
}
}
寻找我发现的dependency injection解决方案,它允许我为使用Cursor
提示用户输入的函数编写测试。它不允许我为confirm()
的每个测试更改Question::new("Continue?").confirm()
函数的用户输入,但我尝试使用条件编译,并提出以下内容。
#[cfg(not(test))]
fn prompt_user<R, W>(mut reader: R, mut writer: W, question: &str) -> Result<String, std::io::Error>
where
R: BufRead,
W: Write,
{
write!(&mut writer, "{}", question)?;
let mut s = String::new();
reader.read_line(&mut s)?;
Ok(s)
}
#[cfg(test)]
fn prompt_user<R, W>(mut reader: R, mut writer: W, question: &str) -> Result<String, std::io::Error>
where
R: BufRead,
W: Write,
{
use tests;
Ok(unsafe { tests::test_response.to_string() })
}
在tests
模块中,我使用了一个全局变量:
pub static mut test_response: &str = "";
#[test]
fn simple_confirm() {
unsafe { test_response = "y" };
let answer = Question::new("Continue?").confirm();
assert_eq!(Answer::YES, answer);
}
只要我只使用单个线程运行测试,这也有效,但也不再允许我测试真实的用户输入功能。对于如此小的箱子来说这不是一个问题,但它非常混乱。我没有从任何可用的测试库中看到任何解决方案。
答案 0 :(得分:3)
正如Stack Overflow question you linked中所提到的,如果您想要可测试性,通常应该避免硬连接外部依赖项(也就是I / O):
在所有这些情况下,我建议使用依赖注入:
然后,写作时:
最后,在main中实例化生产依赖项,并从那里转发它们。
诀窍,而不是对待:
Environment
结构可能很有用,而不是将大量参数传递给每个函数;但是,只需要一个/两个资源的功能应该明确地明确说明它们使用的是什么,