【发布时间】:2020-03-04 04:32:38
【问题描述】:
我正在为我的第一个 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 输入的函数编写测试。它不允许我将用户输入更改为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()的测试。 -
那么,你是说没有任何参数的
confirm不支持依赖注入?如果是这样,我不确定我们能提供什么帮助。是的,你必须有一些注入依赖项的方法,参数是最简单的,但你也可以有结构字段。是什么阻止您在confirm上使用参数来注入您需要的依赖项? -
我不希望
confirm有任何争论,就像公共界面设计的问题一样。我什至没有想过在confirm中使用结构字段作为prompt_user的参数。我认为这可行,让我在测试时修改输入源。
标签: unit-testing rust