【一起学Rust · 项目实战】命令行IO项目minigrep——测试驱动开发完善功能

简介: 【一起学Rust · 项目实战】命令行IO项目minigrep——测试驱动开发完善功能



前言

经过前面三节的学习,我们的小工具minigrep已经实现了读取指定文件内容,并且为了后期开发和测试的方便,重构了整个项目,使错误处理规整化,模块规范化。本次我们将采用测试驱动开发(以后简称TDD)的模式进行开发,为程序编写几个程序测试用例,测试程序搜索查询字符串并返回匹配的行示例的功能,这些功能会在后面开发过程中用到。

测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。这有助于编写简洁可用和高质量的代码,并加速开发过程。


一、任务目的

了解测试驱动开发模式(TDD),熟悉其开发步骤。使用TDD开发模式,编写我们所需要的测试功能代码,逐步增加软件的功能。

TDD是一个软件开发技术,它遵循如下步骤:

  1. 编写一个失败的测试,并运行它以确保它失败的原因是你所期望的。
  2. 编写或修改足够的代码来使新的测试通过。
  3. 重构刚刚增加或修改的代码,并确保测试仍然能通过。
  4. 从步骤 1 开始重复!

使用TDD开发模式的好处

  1. 有助于驱动代码的设计
  2. 有助于在开发过程中保持高测试覆盖率

二、编写测试失败用例

1.增加测试模块和测试函数

我们仿照创建库时里面自带的测试代码,编写测试模块,在其中我们写了个one_result函数用来测试,其中定义了query搜索关键词和contents内容,模拟我们实际操作中获取到的参数,调用了一个search函数,将刚才的参数传入,并且断言返回的就是关键词那一行的vector。

这里我们传入的关键词是芙蓉,因此,如果search运行正常的话就会返回芙蓉老秋霜,团扇羞网尘。戚姬髡发入舂市,万古共悲辛。

search函数还没写,因此直接编译必然会报错,这里我们希望传入这两个值并且返回关键词所在的行才这么写的,search函数的编写按照我们调用的样子来写。

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn one_result() {
    let query = "芙蓉";
    let contents = "\
中山孺子妾,特以色见珍。虽然不如延年妹,亦是当时绝世人。
桃李出深井,花艳惊上春。一贵复一贱,关天岂由身。
芙蓉老秋霜,团扇羞网尘。戚姬髡发入舂市,万古共悲辛。";
    assert_eq!(vec!["芙蓉老秋霜,团扇羞网尘。戚姬髡发入舂市,万古共悲辛。"], search(query, contents));
    }
}

2.编写search函数

由于这里我们是编写测试错误的用例,要确保程序出错是按照我们所期望的方式出错,因此这里我们在search函数返回一个空的vector,确保代码能够编译,且返回的不是我们所预期的结果,代码如下,

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    vec![]
}

此时我们运行一下测试,结果返回断言的左值不等于右值,说明我们写的代码是没有问题的,在后面我们会修复这个错误,让代码测试通过,如下图

三、修改代码,让代码测试通过

目前测试之所以会失败是因为我们总是返回一个空的 vector。为了让程序能够通过测试,我们需要完善search函数的逻辑,返回正确的结果。search的程序流程图如下

开始定义存储Vector是否到达尾行?结束是否包含关键词yesno

1. 按行读取

Rust提供了可以按行读取文本的方法lines,他的调用方法是

contents.lines()

该方法返回一个数组,其中每一位元素都是文本内容的一行。我们用for循环来读取每一行,并且对每一行进行操作,所以对search函数这样改动

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    for line in contents.lines() {
        // 对文本行进行操作
    }
}

2. 检查关键字

检查关键字实际上就是查找字符串,Rust字符串也提供了可以查找字符串的方法contains,他是这么调用的

contents.contains(keyword)

现在我们将他加入search函数中

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
        for line in contents.lines() {
            // 对文本行进行操作
            if line.contains(query) {
            }
        }
    }

3. 存储搜索结果

现在我们可以遍历完每一行,并且对每一行进行检查是否存在我们要找的关键字,所以现在要考虑的就是怎么把这些包含关键字的行保存并返回。考虑在for循环之外创建一个Vector,每当有符合条件的行就在for循环的判断中加入进去,代码如下

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let mut results = Vec::new();
    for line in contents.lines() {
        if line.contains(query) {
            results.push(line);
        }
    }
    results
}

这里定义了一个可变的Vector类型的变量results,然后在for循环中判断,如果有符合条件的行就把这行加到results中,最后返回results

4. 运行测试

现在我们来运行一下这个测试用例,

可见我们写的search函数是符合条件的,通过了测试。

四、在程序中使用代码

我们的项目主要逻辑都是放在run函数中的,因此我们只需要在run函数中调用search函数,并输出每一行的内容就好了,以下是代码

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let contents = fs::read_to_string(config.filename)?;
    for line in search(&config.query, &contents){
        println!("{}", line);
    }
    Ok(())
}

此时运行程序来看看效果,

输入个比较短的关键字,查看是否能找到所有行

输入一个里面不存在的关键字


总结

现在我们就基本完成了这个小工具的开发,创建了个属于自己的小工具,学习了如何组织程序,驱动测试开发的开发方法,还有一些文件输入输出、生命周期、测试和命令行解析的内容。

到现在为止,这个小工具的主要功能就算是开发完毕了,后续我们将优化处理环境变量和输出标准内容,待续。

作业

到现在为止你已经基本完成这个小案列,请思考以下内容:

  • 对于字符串的操作,比如字符串分割,字符串替换等怎么用Rust来写。
  • 测试驱动开发有什么优点,有哪些步骤。
目录
相关文章
|
1月前
|
运维
【运维基础知识】用dos批处理批量替换文件中的某个字符串(本地单元测试通过,部分功能有待优化,欢迎指正)
该脚本用于将C盘test目录下所有以t开头的txt文件中的字符串“123”批量替换为“abc”。通过创建批处理文件并运行,可实现自动化文本替换,适合初学者学习批处理脚本的基础操作与逻辑控制。
134 56
|
11天前
|
网络协议 关系型数据库 应用服务中间件
【项目场景】请求数据时测试环境比生产环境多花了1秒是怎么回事?
这是一位粉丝(谢同学)给V哥的留言,描述了他在优化系统查询时遇到的问题:测试环境优化达标,但生产环境响应时间多出1秒。通过抓包分析,发现MySQL请求和响应之间存在500毫秒的延迟,怀疑是网络传输开销。V哥给出了以下优化建议:
|
1月前
|
测试技术
自动化测试项目实战笔记(三):测试用户注册(验证码错误,成功,出现弹框时处理)
本文是关于自动化测试项目实战笔记,主要介绍了如何测试用户注册功能,包括验证码错误、注册成功以及弹框处理的测试步骤和代码实现。
85 2
自动化测试项目实战笔记(三):测试用户注册(验证码错误,成功,出现弹框时处理)
|
1月前
|
测试技术
自动化测试项目学习笔记(五):Pytest结合allure生成测试报告以及重构项目
本文介绍了如何使用Pytest和Allure生成自动化测试报告。通过安装allure-pytest和配置环境,可以生成包含用例描述、步骤、等级等详细信息的美观报告。文章还提供了代码示例和运行指南,以及重构项目时的注意事项。
170 1
自动化测试项目学习笔记(五):Pytest结合allure生成测试报告以及重构项目
|
1月前
|
Java 关系型数据库 MySQL
自动化测试项目实战笔记(一):JDK、Tomcat、MySQL、Jpress环境安装和搭建
这篇文章是关于自动化测试项目实战笔记,涵盖了JDK、Tomcat、MySQL、Jpress环境的安装和搭建过程,以及测试用例和常见问题总结。
49 1
自动化测试项目实战笔记(一):JDK、Tomcat、MySQL、Jpress环境安装和搭建
|
1月前
|
测试技术 Python
自动化测试项目学习笔记(四):Pytest介绍和使用
本文是关于自动化测试框架Pytest的介绍和使用。Pytest是一个功能丰富的Python测试工具,支持参数化、多种测试类型,并拥有众多第三方插件。文章讲解了Pytest的编写规则、命令行参数、执行测试、参数化处理以及如何使用fixture实现测试用例间的调用。此外,还提供了pytest.ini配置文件示例。
26 2
|
1月前
|
人工智能 算法 测试技术
自动化测试项目实战笔记(二):解决验证码识别问题
这篇文章介绍了三种自动化测试中验证码识别的方法:使用Python的pytesseract和PIL模块、利用第三方API如万维易源,以及使用开源的ddddocr库,还提到了一些注意事项,比如如何获取验证码区域的截图。
64 2
|
1月前
|
测试技术 数据安全/隐私保护 Python
自动化测试项目实战笔记(四):测试用户登录(账号密码错误,成功,出现弹框等情况)
本文介绍了使用Selenium进行自动化测试时如何测试用户登录的不同情况,包括账号密码错误、登录成功以及处理登录时出现的弹框,并提供了相应的Python代码实现。
53 0
自动化测试项目实战笔记(四):测试用户登录(账号密码错误,成功,出现弹框等情况)
|
1月前
|
测试技术 Python
自动化测试项目学习笔记(二):学习各种setup、tearDown、断言方法
本文主要介绍了自动化测试中setup、teardown、断言方法的使用,以及unittest框架中setUp、tearDown、setUpClass和tearDownClass的区别和应用。
61 0
自动化测试项目学习笔记(二):学习各种setup、tearDown、断言方法
|
1月前
|
测试技术
Appscan手工探索、手工测试功能实战
Appscan手工探索、手工测试功能实战