使用Insta进行Rust快照测试

coding到灯火阑珊 发表于 4月以前  | 总阅读数:347 次

Rust有很多测试策略,从单元测试到集成测试。在本文中,我们将探索使用Insta进行快照测试,了解它如何补充你的开发工作。

什么是快照测试?

快照测试是一种通过将输出与一组已定义的预期值进行比较来验证代码正确性的方法。例如,如果你以前编写过集成测试,那么可以将部分测试视为快照,因为你正在将预期结果与实际输出进行比较。

默认情况下,Rust使用assert_eq!函数,但它只允许你与原始Rust类型进行比较。快照测试要求对更复杂的数据结构进行比较。

通常,快照测试是在前端而不是后端完成的,因为前端应用程序返回HTML而不是常规字符串。比较输出更省时,而不是解析HTML并检查每个特定元素。

在测试整个程序的输出时,你可以充分利用快照测试,从而测试网页中的更多元素,而不必担心所有结果是否一致。

Insta是什么?

Insta是一个Rust应用程序的快照测试库,提供了一个简单而直观的界面来运行和更新测试:

正如上面的截图中看到的,在Insta中调试测试非常容易,并且在Insta CLI的帮助下,你可以很容易地用新的测试输出更新所有失败的测试输出。请记住,不应该在每次失败时都更新结果。应该只在更改某个测试的代码输出时才实现更新,从而将代码更新导致的错误数量降至最低。

Insta仅通过Serde支持CSV、JSON、TOML、YAML和RON文件,Serde是一个数据序列化库,可以将各种类型的数据结构编码为更紧凑的格式,反之亦然。

Insta如何工作的?

Insta有许多不同类型的支持。如前所述,可以使用Insta对JSON文件、CSV文件甚至YAML文件进行快照测试。但有趣的是,Insta宏是如何在底层运行的。

Insta使用Serde提供多文件支持。不过,Insta并没有将它们分割成更小的包,而是依靠Cargo的功能将所有包无缝地打包为一个包,因此客户端可以通过这些功能只下载他们需要的包:

// Cargo.toml
[features]
csv = ["dep_csv", "serde"]
json = ["serde"]
ron = ["dep_ron", "serde"]
toml = ["dep_toml", "serde"]
yaml = ["serde"]

Insta快照断言库只比较两个字符串。因此,只需要传递SerializationFormat,assert_snapshot!宏将编译和正常工作。

Insta VS. assert_eq

在底层,Insta和assert_eq都做序列化以外的事情。这两种断言解决方案之间最大的区别是Insta本地支持序列化。而在使用assert_eq!时,必须使用Serde进行手动序列化,以实现与Insta相同的结果。

即使在assert_snapshot函数内部,Insta也会进行简单的字符串与字符串比较。使用assert_eq将实现类似的结果。assert_eq的比较过程比Insta的要轻量级得多,与直接使用Insta相比,比较Insta和assert_eq并不理想,因为它需要大量的样板代码和额外的工作。

开始使用Insta

使用Insta很简单,像任何其他库一样,打开Cargo.toml文件。并添加相关依赖项。如下所示,将同时添加Insta和Serde:

// Cargo.toml // ...
[features]
json = ["serde"]

[dependencies]
serde = { version = "1.0.117", optional = true }

[dev-dependencies]
insta = { version = "1.26.0", features = ["json", "csv"] }
serde = { version = "1.0.117", features = ["derive"] }

在本例中,我们将使用json和csv特性来编写一个简单的程序,供你测试。我们将创建一个简单的待办事项列表CLI应用程序来跟踪任务。

首先,在src/main.rs文件中创建一个基本样板:

use std::env;
use std::io::{self, BufRead};
use std::path::Path;

struct Task {
    name: String,
    is_completed: bool,
}

fn readline() -> String {
    let mut strr: String = "".to_string();
    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        strr = line.unwrap().to_string();
        break;
    }
    strr
}

fn main() -> io::Result<()> {
    let mut tasks: Vec<Task> = vec![];
    while true {
        println!("{}", readline());
        break;
    }
    Ok(())
}

fn add_task(tasks: &Vec<Task>, name: String) -> io::Result<()> {
    // TODO: Add logic
    Ok(())
}

fn list_tasks(tasks: &Vec<Task>) -> io::Result<()> {
    // TODO: List logic
    Ok(())
}

fn complete_task(tasks: &Vec<Task>, level: i32) -> io::Result<()> {
    // TODO: Complete logic
    Ok(())
}

这个简单的CLI允许用户创建或更新新任务。代码的结构可能会让你了解我们将使用Insta做什么。但是,我们先别太超前了。

接下来,我们将定义每个函数以添加更多结构,重点关注main函数:

fn main() -> io::Result<()> {
    let mut tasks: Vec<Task> = vec![];
    loop {
        list_tasks(&tasks);

        let option = readline();
        _ = match option.as_str() {
            "1" => {
                println!("Enter new task name: ");
                let name = readline();
                add_task(&mut tasks, name);
            },
            "2" => {
                println!("Enter task to complete: ");
                let level: i32 = readline().parse::<i32>().unwrap();
                complete_task(&mut tasks, level);
            },
            _ => break,
        };
    }
    Ok(())
}

main函数将命令重定向到应用程序的其他函数,可以列出任务、创建新任务或完成任务。为了简化事情,现在我们还没有任务移除函数。但是,如果需要,可以稍后实现。

由于可变tasks向量是从main函数传递给add_task函数的,所以你可以使用.push修饰符向向量中添加一个新task:

fn add_task(tasks: &mut Vec<Task>, name: String) -> io::Result<()> {
    tasks.push(Task {
        name: name,
        is_completed: false,
    });
    Ok(())
}

每次打开CLI时,都需要列出任务列表。List_tasks已经在main函数中的循环开始时声明;你所需要做的就是定义它。为了简化,将任务向量传递给list_tasks函数。然后,遍历它们并打印它们的名称和状态:

fn list_tasks(tasks: &Vec<Task>) {
    for _ in 0..50 {
        println!("\n");
    }
    println!("Tasks List: ");
    for task in tasks {
        println!("Name: {}", task.name);
        println!("Is Completed: {}", task.is_completed);
    }
    println!("Choose the following options:
1. Add tasks
2. Complete tasks
3. Exit");
}

Rust没有为cli提供清晰的屏幕选项,你可以通过打印50次换行来解决这个问题。

最后,要更新任务,只需更新它们的状态。可以直接访问vector对象并修改is_completed属性:

fn complete_task(tasks: &mut Vec<Task>, level: i32) -> io::Result<()> {
    tasks[level as usize].is_completed = true;
    Ok(())
}

现在,尝试运行应用程序,应该能够创建和完成新的任务。输入cargo run,应该看到如下内容:

Tasks List: 
Choose the following options:
1. Add tasks
2. Complete tasks
3. Exit

它不是最复杂的应用程序,但对于我们的教程,它是可行的。输入1并按回车,将任务重命名为new task,将收到以下信息:

Tasks List: 
Name: new task
Is Completed: false
Choose the following options:
1. Add tasks
2. Complete tasks
3. Exit

现在已经验证了一切都在运行,可以继续进行快照测试了。

Serde是可选依赖项,Insta是开发依赖项,所以不能将它们包含在主应用程序上下文中。必须在它们前面加上一个#[cfg(test)]宏。多个Task结构如下所示:

#[cfg(test)]
#[derive(serde::Serialize, Clone)]
struct Task {
    pub name: String,
    pub is_completed: bool,
}

#[cfg(not(test))]
#[derive(Clone)]
struct Task {
    pub name: String,
    pub is_completed: bool,
}

测试中使用的Task将是可序列化和可克隆的,因此我们可以存储同一对象的多个副本而不会破坏它。我们将在测试时使用#[cfg(test)]结构,在使用cargo run运行项目时使用#[cfg(not(test))]结构。我们将为不同的上下文分离结构;虽然这不是最好的做法,但它会节省时间来专注于更重要的Insta测试。

为了使add_task和complete_task可测试,它们必须在每次运行时返回一个Task结构体:

fn add_task(tasks: &mut Vec<Task>, name: String) -> io::Result<Task> {
    let task = Task {
        name,
        is_completed: false,
    };
    tasks.push(task.clone());
    Ok(task)
}

// fn list_tasks....

fn complete_task(tasks: &mut Vec<Task>, level: i32) -> io::Result<Task> {
    tasks[level as usize].is_completed = true;
    Ok(tasks[level as usize].clone())
}

前面添加的Clone派生将用于add_task函数。使用Insta编写这个函数的单元测试,在文件的底部,添加以下代码:

#[cfg(test)]
extern crate insta;

然后,像这样测试add_task函数:

#[cfg(test)]
mod tests {
    use super::*;
    use insta::{assert_json_snapshot, assert_compact_json_snapshot, assert_csv_snapshot};

    #[test]
    fn test_json_add_task_struct_vec() {
        let mut tasks: Vec<Task> = vec![];

        let task: Task = add_task(&mut tasks, "name".to_string()).unwrap();
        assert_json_snapshot!(task, @r###"{
  "name": "name",
  "is_completed": false
}"###);
        assert_compact_json_snapshot!(task, @r###"{"name": "name", "is_completed": false}"###);

        assert_json_snapshot!(tasks, @r###"[
  {
    "name": "name",
    "is_completed": false
  }
]"###);
        assert_compact_json_snapshot!(tasks, @r###"[{"name": "name", "is_completed": false}]"###);
    }
}

通过在终端或命令提示符中执行cargo test命令来运行测试。所有测试都应该成功通过,这样就完成了 !

总结

当开发人员编写应用程序时,他们会面临测试的挑战。在理想的情况下,我们能够运行我们的代码,并确保在将其部署到生产环境之前按预期工作。然而,现实世界的软件开发远非理想,因此测试是我们开发工作流程的重要组成部分。在开发任务关键型系统或应用程序时尤其如此,在这些系统或应用程序中,失败是不可避免的。

除了使用C/C++之外,Rust等提供严格类型系统的语言正日益成为任务关键型应用程序的一种选择,特别是在考虑速度和内存安全的情况下。

快照测试通过验证输出来帮助你验证代码的正确性。如果你正在管理一个不断变化的代码库,这是非常有用的,这样就可以在进行更新或更改时发现是否有什么问题。

Insta提供了围绕常见断言宏的包装器,供在程序中使用。通过对它们进行测试,并根据项目需求为部署做好准备,Insta可以处理你需要完成的大部分样板代码。

本文由微信公众号coding到灯火阑珊原创,哈喽比特收录。
文章来源:https://mp.weixin.qq.com/s/Mp_wL20NELBZP55D-TJMjA

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:8月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:8月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:8月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:8月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:8月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:8月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:8月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:8月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:8月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:8月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:8月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:8月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:8月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:8月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:8月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:8月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:8月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:8月以前  |  398次阅读  |  详细内容 »
 相关文章
 目录