20.1、单元测试
基本的单元测试,可以在系统测试之前,把大部分比较低级的错误都消灭掉。
什么是单元测试
【引用百度百科】
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。
单元测试好处
消灭低级错误
基本的单元测试,可以在系统测试之前,把大部分比较低级的错误都消灭掉,减少系统测试过程中的问题,这样也就减少了系统测试中定位和解决问题的时间成本了。
找出潜在的bug
某些类型的bug,靠系统测试是很难找到的。例如一些代码分支,平时99%的场景基本上都走不到,但一旦走到了,如果没有提前测试好,那么可能就是一个灾难。
上库前的保证
加了新代码,上库前跑一把单元测试,都通过,说明代码可能没有影响到之前的逻辑,这样上库也比较放心。如果之前的单元测试跑不过,那么很有可能新的代码有潜在的问题,赶紧修复去吧。
重构代码的机会
写单元测试的过程中,你可能会顺手把一些code重构了,为什么?举例,一些长得非常像的代码,如果每次都要写一堆测试代码去测同样的code,你会不会抓狂?不测吧,覆盖率又上不去,于是我就会想方设法把待测试的code改得尽量的精简,重复代码减少,这样覆盖率上去了,测试也好测了,代码也简洁了。如果没有单元测试和覆盖率的要求的话,坦白说可能一来自己不会发现这些重复的code,另一方面即使发现了,可能也没有太大的动力去改进。
另外,由于单元测试中,你需要尝试去覆盖一些异常分支,这是系统测试常常走不到的地方,于是就会引起你的一些思考,例如这个异常分支是否真的需要?是否真的会发生?对于一些实际上绝对不会出错的函数,那么我觉得可能异常分支是没必要存在的。
重新review代码的机会
写UT的过程中,我总是会好好看哪些代码执行到了,哪些代码没有执行到,这其实也是一个review自己代码的机会,有些时候,并不是UT本身帮我找到bug,而是回头review自己代码的时候发现的。
单元测试几种类型
基于API接口测试(白盒+浅度黑盒测试)
基于项目代码测试(深度黑盒测试)
主流的单元测试包
MSTest(推荐,和VS 2019 深度集成)
NUnit
xUnit(最流行的库)
SpecFlow 和 Gherkin
什么是 SpecFlow
SpecFlow 是.Net平台的BDD工具,可以用自然语言编写测试用例,根据这些测试案例,能够根据测试案例生成测试类、测试方法及到处测试汇总报表。
行为驱动开发(Behavior-Driven Development)(简写BDD),在软件工程中,BDD是一种敏捷软件开发的技术。
什么是Gherkin-BDD语言?
在了解Gherkin之前,有必要了解跨项目不同领域的公共语言的重要性和需求。我所说的不同领域是指客户、开发人员、测试人员、业务分析师和管理团队。让我们先讨论开发项目中的常见问题,然后再讨论解决方案,在此过程中,我们将遇到对公共语言的需求。
所以为了能够让不同领域的任意都能够了解业务功能,就有了 Gherkin。
Gherkin使用一组特殊的关键字来为可执行规范赋予结构和意义,而且文件后缀名为:*.feature
,如 test_hoa_author.feature
:
Gherkin 常用的关键字
Feature
:目的是提供软件特性的高级描述,并对相关场景进行分组。Scenario/Scenario Outline
:我们具体的测试案例场景,建议统一用Scenario Outline
Given
:用于描述系统的初始上下文—场景,也就是我们常说的假设
And
:是对Given
的一个补充When
:描述动作或行为Then
:用于描述预期的结果But
:对 Then 的一个补充Examples
:指定多个假设,也就是同一个场景不同的假设条件。@mytag
:用来指定场景唯一标识,没实际作用,只做标识。#
:编写注释
Gherkin 传递参数
Gherkin 默认能够对 关键字中的描述带有 数值类型的符号进行解析成参数,如:
此时,Scenario
中的 Given/And/Then
中包含的 50、70、120
会自动解析成参数。
2. 传递字符串参数,如果需要传入字符串类型,只需要采用 三引号即可:"""
,如:
此时,Powered by Monk
就会自动生成字符串参数。
3. 传递对象、数组类型参数,采用类似 Markdown 表格的写法,如:
4. 相同测试场景,不同的测试参数,如:
正常写法:
推荐写法:
特别注意👈👈👈
在 Given/And/When/Then/But 中,必须与 "." 作为语句结束符,不如会生成不正确的语句。👈👈👈
更多 Gherkin 知识可查阅官方文档。
编写单元测试
前期配置
第一步
在 Visual Studio 2019 中安装 SpecFlow for Visual Studio 2019
安装后重启 Visual Studio 2019 即可。
第二步
在Visual Studio 2019 中登录你的 outlook.com 账号,然后选择 Hoa.MSTest.Remote
项目,右键选择 运行测试
,然后在 输出
工具栏设置 显示输出来源
为 测试
。就会看到有这样一串链接,点击注册即可,注册成功后重启 Visual Studio 即可完成单元测试基础配置操作。
编写测试案例
在 Hoa.MSTest.Remote
项目下的 Features
文件夹下创建 .feature
文件,如,这里我们测试 /api/Member/PersonalDetails
接口,我们可以创建命为:Test_Api_Member_PersonalDetails.feature
假设我们要测试以下案例:👈👈👈
接口能够正常访问
如果不输入
userName
参数,提示 “用户名不能为空”如果输入错误的 `userName 参数,提示 “用户不存在”
如果输入正确的 `userName" 参数:snrcsoft,则正确返回数据
这时,我们就可以将 上述的测试案例翻译成 Gherkin
语言,如:
生成单元测试类
在 Test_Api_Member_PersonalDetails.feature
右键选择 Generate Step Definitions
,并在弹出的菜单中点击 Preview
预览是否正确生成,确保没问题后点击 Generate
按钮,并生成到 Hoa.MSTest.Remote
的 StepDefinitions
文件夹中。
编写测试断言
第一步
在 Hoa.MSTest.SpecFlow
的 Materials
文件夹下的 IApi_Hoa.cs
中添加下列代码:
第二步
在刚刚生成的 TestUrlApiMemberPersonalDetailsSteps.cs
中写入以下代码:
运行单元测试
点击 Visual Studio 2019 顶部 测试
菜单,点击 运行所有测试
并打开 测试资源管理器
,并点击运行所有测试即可看到所有结果。
关于断言
断言是编程术语,表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真,可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言而在部署时禁用断言。
同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。 使用断言可以创建更稳定、品质更好且 不易于出错的代码。当需要在一个值为FALSE时中断当前操作的话,可以使用断言。单元测试必须使用断言。
断言有几种方式
在Hoa Framework 中支持两种断言方式:
Assert:这时是MSTest 单元测试默认自带的,在
Microsoft.VisualStudio.TestTools.UnitTesting
命名空间下FluentAssertions:这时第三方提供的库,拥有非常丰富的断言方法,推荐使用
回归测试
回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。自动回归测试将大幅降低系统测试、维护升级等阶段的成本。
回归测试作为软件生命周期的一个组成部分,在整个软件测试过程中占有很大的工作量比重,软件开发的各个阶段都会进行多次回归测试。在渐进和快速迭代开发中,新版本的连续发布使回归测试进行的更加频繁,而在极端编程方法中,更是要求每天都进行若干次回归测试。因此,通过选择正确的回归测试策略来改进回归测试的效率和有效性是很有意义的。
如何进行回归测试
在 Hoa Framework 中,只需要重新 运行所有测试即可。
导出报表
在 Hoa Framework 框架中,只要运行过单元测试,就会自动在 项目根目录下生成 TestResults
文件夹,里面就包含了所有测试的数据报表。
常见错误
All steps are bound!
这个提示的意思是此测试案例已经生成过测试类了,无需再次生成,如果需要强制生成,只需要在运行中输入:%TEMP%
,然后在打开的文件夹中搜索 specflow
并删除带 specflow
的文件即可。
关于项目代码测试
在 Hoa Framework 框架中,支持两种单元测试,上面一种是基于接口测试,还有一种是基于项目代码测试,主要是在 Hoa.MSTest.Projects
项目层中,该层已经自动引用了 Hoa, Hoa.Application, Hoa.Core, Hoa.EntityFramework.Core
项目引用了,只需要手动编写测试调用代码即可。
由于我们大量用到了依赖注入方式创建对象,所以框架默认引如了 Moq 第三方组件,可以快速帮助我们创建对象的所有依赖。
最后更新于