我正在为我的第一个Rust项目编写一个CLI问题询问库,因为我很可能会在其中使用它。我无法找到一种干净的测试
只要我使用单个线程运行测试,这个方法就可以工作,但是也不再允许我测试真实的用户输入函数。对于这样一个小的箱子来说,这并不是问题,但非常混乱。我没有看到任何可用的测试库中有解决此问题的方案。
终端
方法的建造者模式的方法,该方法使用配置获取用户输入并返回答案。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();
}
}
}
在寻找解决方案时,我发现了依赖注入,它使我能够为使用Cursor
提示用户输入的函数编写测试。然而,它不允许我为Question::new("Continue?").confirm()
的每个测试更改用户输入到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);
}
只要我使用单个线程运行测试,这个方法就可以工作,但是也不再允许我测试真实的用户输入函数。对于这样一个小的箱子来说,这并不是问题,但非常混乱。我没有看到任何可用的测试库中有解决此问题的方案。
reader
、writer
和question
都是在confirm()
函数内部传递给prompt_user()
的,所以我无法像测试prompt_user()
函数本身时那样操纵输入。因此,我无法自动化涉及confirm()
的测试。 - datenstromconfirm
不支持依赖注入吗?如果是这样,我不确定我们能提供什么帮助。是的,您必须有一种注入依赖项的方式,参数是最简单的方法,但您也可以使用结构字段。是什么阻止您在confirm
上使用参数来注入所需的依赖项呢? - Shepmasterconfirm
有任何参数,这只是出于公共接口设计的考虑。但我从未想过在confirm
中使用结构字段作为prompt_user
的参数。我认为这可能可行,并且可以让我在测试时修改输入源。 - datenstrom