【一起学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来写。
  • 测试驱动开发有什么优点,有哪些步骤。
目录
相关文章
|
2天前
|
NoSQL 关系型数据库 MySQL
软件测试之【基于开源商城系统fecmall功能测试项目实例】
软件测试之【基于开源商城系统fecmall功能测试项目实例】
软件测试之【基于开源商城系统fecmall功能测试项目实例】
|
2天前
|
IDE 前端开发 时序数据库
【Docker项目实战】使用Docker部署speedtest-tracker速度测试追踪器
【6月更文挑战第4天】使用Docker部署speedtest-tracker速度测试追踪器
4 0
|
2天前
|
测试技术
软件测试项目式学习三(软件测试原则与基本流程与实际测试用例)
软件测试项目式学习三(软件测试原则与基本流程与实际测试用例)
|
2天前
|
测试技术 程序员 开发者
软件测试项目式学习一(认识软件生命周期与开发模型及软件质量)
软件测试项目式学习一(认识软件生命周期与开发模型及软件质量)
|
2天前
|
安全 测试技术
软件测试项目式学习二(认识软件测试及软件测试分类与案例分析)
软件测试项目式学习二(认识软件测试及软件测试分类与案例分析)
|
3天前
|
测试技术 UED
课题项目结题测试通常会采用的方法
课题项目结题测试确保项目准确稳定,涉及功能测试、性能测试、安全测试、兼容性测试、用户验收测试及文档审查。这些方法全面评估项目各方面,保证顺利交付和使用,同时促进项目改进。标签:结题测试、软件测试报告。
课题项目结题测试通常会采用的方法
|
10天前
|
安全 数据管理 测试技术
网络安全与信息安全:防范漏洞、加强加密与提升安全意识深入探索自动化测试框架的设计原则与实践应用化测试解决方案。文章不仅涵盖了框架选择的标准,还详细阐述了如何根据项目需求定制测试流程,以及如何利用持续集成工具实现测试的自动触发和结果反馈。最后,文中还将讨论测试数据管理、测试用例优化及团队协作等关键问题,为读者提供全面的自动化测试框架设计与实施指南。
【5月更文挑战第27天】 在数字化时代,网络安全与信息安全已成为维护国家安全、企业利益和个人隐私的重要环节。本文旨在分享关于网络安全漏洞的识别与防范、加密技术的应用以及提升安全意识的重要性。通过对这些方面的深入探讨,我们希望能为读者提供一些实用的建议和策略,以应对日益严峻的网络安全挑战。 【5月更文挑战第27天】 在软件开发周期中,自动化测试作为保障软件质量的关键步骤,其重要性日益凸显。本文旨在剖析自动化测试框架设计的核心原则,并结合具体案例探讨其在实际应用中的执行策略。通过对比分析不同测试框架的优缺点,我们提出一套高效、可扩展且易于维护的自动
|
13天前
|
Android开发 数据安全/隐私保护 iOS开发
ios和安卓测试包发布网站http://fir.im的注册与常用功能
ios和安卓测试包发布网站http://fir.im的注册与常用功能
12 0
ios和安卓测试包发布网站http://fir.im的注册与常用功能
|
17天前
|
XML Java 测试技术
Maven 构建 & 项目测试
本节介绍了如何使用Maven进行Java应用的构建和测试。在`C:\MVN\consumerBanking`项目中,`pom.xml`配置了JUnit测试框架。执行`mvn clean package`命令,Maven会清理目标目录、编译源码和测试代码,运行测试,并生成`consumerBanking-1.0-SNAPSHOT.jar`。测试报告位于`surefire-reports`目录。
|
21天前
|
移动开发 前端开发 JavaScript
uniapp中IO模块(管理本地文件系统)的常用功能封装
uniapp中IO模块(管理本地文件系统)的常用功能封装