pix*_*nil 5 unit-testing rust serde
我有一个在字段上实现Deserialize和使用的结构serde(deserialize_with):
#[derive(Debug, Deserialize)]
struct Record {
name: String,
#[serde(deserialize_with = "deserialize_numeric_bool")]
is_active: bool,
}
Run Code Online (Sandbox Code Playgroud)
将deserialize_numeric_bool字符串“ 0”或“ 1”反序列化为相应的布尔值的实现:
pub fn deserialize_numeric_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
where D: Deserializer<'de>
{
struct NumericBoolVisitor;
impl<'de> Visitor<'de> for NumericBoolVisitor {
type Value = bool;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("either 0 or 1")
}
fn visit_u64<E>(self, value: u64) -> Result<bool, E>
where E: DeserializeError
{
match value {
0 => Ok(false),
1 => Ok(true),
_ => Err(E::custom(format!("invalid bool: {}", value))),
}
}
}
deserializer.deserialize_u64(NumericBoolVisitor)
}
Run Code Online (Sandbox Code Playgroud)
(我感谢有关代码改进的评论)
我想为反序列化功能(如)编写单元测试deserialize_numeric_bool。当然,我友好的搜索框显示了serde_test板条箱和有关单元测试的文档页面。但是这些资源对我而言无济于事,因为板条箱测试了直接实现的结构Deserialize。
我的一个想法是创建一个仅包含反序列化函数的输出的新类型,并对其进行测试。但这对我来说似乎是不必要的间接方式。
#[derive(Deserialize)]
NumericBool {
#[serde(deserialize_with = "deserialize_numeric_bool")]
value: bool
};
Run Code Online (Sandbox Code Playgroud)
如何为此编写惯用测试?
我当前的解决方案仅使用 已提供的结构serde。\n在我的用例中,我只想测试给定的字符串是否成功反序列化为 bool 或存在特定错误。为基本数据类型提供serde::de::value简单的反序列化器,例如U64Deserializer保存u64. 它还具有一个Error 结构,为错误特征 \xe2\x80\x93 提供最小表示,可用于模拟错误。
我的测试目前看起来是这样的:我用反序列化器模拟输入并将其传递给我的正在测试的函数。我喜欢的是我不需要在那里间接,并且我没有额外的依赖项。它不如assert_tokens*提供的那么好serde_test,因为它需要错误结构并且感觉不太精致。但对于我的情况,只有一个值被反序列化,它满足了我的需求。
use serde::de::IntoDeserializer;\nuse serde::de::value::{U64Deserializer, StrDeserializer, Error as ValueError};\n\n#[test]\nfn test_numeric_true() {\n let deserializer: U64Deserializer<ValueError> = 1u64.into_deserializer();\n assert_eq!(numeric_bool(deserializer), Ok(true));\n}\n\n#[test]\nfn test_numeric_false() {\n let deserializer: U64Deserializer<ValueError> = 0u64.into_deserializer();\n assert_eq!(numeric_bool(deserializer), Ok(false));\n}\n\n#[test]\nfn test_numeric_invalid_number() {\n let deserializer: U64Deserializer<ValueError> = 2u64.into_deserializer();\n let error = numeric_bool(deserializer).unwrap_err();\n assert_eq!(error.description(), "invalid bool: 2");\n}\n\n#[test]\nfn test_numeric_empty() {\n let deserializer: StrDeserializer<ValueError> = "".into_deserializer();\n let error = numeric_bool(deserializer).unwrap_err();\n assert_eq!(error.description(), "invalid type: string \\"\\", expected either 0 or 1");\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我希望它也能帮助其他人或激励其他人找到更精美的版本。
\n我最近在尝试解决类似问题时多次遇到这个问题。对于未来的读者来说,pixunil 的答案很好,很直接,而且效果很好。但是,我想提供一个使用serde_test 的解决方案,如单元测试文档中提到的那样。
我研究了如何在几个箱子中使用serde_test ,这些箱子是通过其对lib.rs 的反向依赖关系找到的。正如您在原始帖子中提到的,其中一些定义了用于测试反序列化或序列化的小型结构或枚举。我想这样做是惯用的,否则测试会太冗长。
这里有几个例子;这是一个非详尽的列表:
不管怎样,假设我有一个从 a反序列化 a 的函数和另一个boolu8将 a 序列化为a 的函数。boolu8
use serde::{
de::{Error as DeError, Unexpected},
Deserialize, Deserializer, Serialize, Serializer,
};
fn bool_from_int<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
match u8::deserialize(deserializer)? {
0 => Ok(false),
1 => Ok(true),
wrong => Err(DeError::invalid_value(
Unexpected::Unsigned(wrong.into()),
&"zero or one",
)),
}
}
#[inline]
fn bool_to_int<S>(a_bool: &bool, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if *a_bool {
serializer.serialize_u8(1)
} else {
serializer.serialize_u8(0)
}
}
Run Code Online (Sandbox Code Playgroud)
struct我可以通过在我的模块中定义 a 来测试这些函数test。这允许将测试专门限制在这些函数上,而不是序列化/反序列化更大的对象。
#[cfg(test)]
mod tests {
use super::{bool_from_int, bool_to_int};
use serde::{Deserialize, Serialize};
use serde_test::{assert_de_tokens_error, assert_tokens, Token};
#[derive(Debug, PartialEq, Deserialize, Serialize)]
#[serde(transparent)]
struct BoolTest {
#[serde(deserialize_with = "bool_from_int", serialize_with = "bool_to_int")]
a_bool: bool,
}
const TEST_TRUE: BoolTest = BoolTest { a_bool: true };
const TEST_FALSE: BoolTest = BoolTest { a_bool: false };
#[test]
fn test_true() {
assert_tokens(&TEST_TRUE, &[Token::U8(1)])
}
#[test]
fn test_false() {
assert_tokens(&TEST_FALSE, &[Token::U8(0)])
}
#[test]
fn test_de_error() {
assert_de_tokens_error::<BoolTest>(
&[Token::U8(14)],
"invalid value: integer `14`, expected zero or one",
)
}
}
Run Code Online (Sandbox Code Playgroud)
BoolTest位于按通常方式tests门控的模块内。#[cfg(test)]这意味着它BoolTest只是为了测试而编译,而不是增加膨胀。我不是 Rust 专家,但我认为如果程序员希望用作serde_test工具,这是一个很好的选择。
| 归档时间: |
|
| 查看次数: |
95 次 |
| 最近记录: |