系列目录
Nunit 测试基础之简单断言
在开始本篇之前需要补充一些内容, 通过前面搭建 Nunit 测试环境我们知道要使一个方法成为单元测试方法首先要在此方法所在类加上 TestFixture 注解, 并且在该方法上添加上 Test 注解.
然而还有一点需要注意: 所有进行单元测试的方法必须标识为 public 访问级别, 否则无法识别为单元测试方法
此外, 单元测试方法还有以下特征
单元测试方法不带返回参数, 也即都是 Void 类型
由于单元测试方法都是用来断言特定状态的, 因此返回值是没有意义的. 此外也不要尝试在一个单元测试方法中调用另一个, 这样做违反的单元测试的初衷(一旦出现错误不知道是哪个方法出出现的, 还需要借助单元调用去发现是哪个方法出现的)
单元测试方法不能带有参数
这里说的不能是指不能像普通方法一样带有普通的参数(可以带基于注解的特殊参数)
如果像普通方法一样带参数, 虽然编译能通过, 但是运行时会抛出异常.
单元测试方法不能重载
这里说的不能是不应该, 实践中是可以的, 但是重载方法会带来无尽的麻烦, 读者可以自己实践一下.
进行单元测试无非就是对不同参数引起方法出现不同结果的断言 (一般情况下所有的单元测试方法都有断言) 下面我们来看 Nunit 中最基本最常用的断言
基本断言
Assert.True()
Assert.True 用于断言布尔参数是否为 true
Assert.True 的重载方法还支持可空布尔参数
Assert.True 还支持自定义错误提示
上面代码改为如下
如果返回错误的时候, 我们自定义的错误信息就会显示出来.
其它的断言方法也大都有此重载
Assert.IsTrue
此断言方法为 Assert.True 的亲兄弟, 二者功能一模一样.
ssert.False
与 Assert.True 断言状态相反, 断言某一参数的结果为 false
这里需要特别说明的是, 单元测试应该力求简单, 明了, 断言尤其如此.
上面的断言还也可以写成
Assert.False(!firstCondition);
这和断言变量 firstCondition 为 true 最终功能一样, 但是看上去很不直接明了, 通常情况下我见到 Assert.False 第一反应就是断言一个变量为 False, 这里则反其道行之, 实际上是断言一个变量为 true, 这种情况应当避免.
Assert.IsFalse
Assert.False 的亲兄弟, 二者表现一模一样
Nunit Assert 类还有还多其它的前面带有 Is 的方法, 它们都和不带 Is 的一模一样, 其中带 Is 的是为了兼容老版本写法.
Assert.Null
用于断言一个变量是否为 null, 这里不再举例, 但是实际中用的却比较多.
Assert.NotNull
用于断言一个变量不是 null, 它和 Assert.Null()功能相同, 只是断言的状态相反.
Nunit 里还有其它的前缀有 Not 的方法, 它和不带 Not 的方法用法一样, 只是断言的状态相反
Assert.Throws
用于断言特定方法在运行的时候会抛出异常. 此方法有泛型版本, 异步版本, 这里仅对异步版本进行说明
由于示例越来越复制, 我们不能只在测试方法内写一些简单代码进行测试了, 这里我们新建一个 Person 类如下
这个类里面包含一个 WhetherNameContainsB 方法, 用于判断实例的 Name 是否包含字母 B,
这个方法里面有三个逻辑分支, 单元测试的时候每一个都要覆盖到, 这里我们断言如果 name 为 null 则抛出 ArgumentNullException
我们编写如下单元测试方法
运行这个测试, 则会返回成功状态, 因为预期的异常出现了.
Assert.IsEmpty
用于断言字段串是否为空字符串.
Assert.Positive
用于断言数字类型 (int,long,float,double,decimal 等) 为正数(大于零的数)
其实很多断言都可以断言都可以用 Assert.True 来完成, 比如断言一个数是否为正数, 可以用 Assert.True(a>0), 这里由于 a 只是一个普通变量, 使用 a>0 作为条件主义仍然十分清析, 然而到了后面有我们不仅要判断一个变通变量, 还要判断 lambda 表达式, 如果条件过于复杂, 则语义会变得不是特别清析了, 使用 Assert 自带的静态方法主义会更加清析, 可读性更高.
Assert.Negative
用于断言数字类型为负数(小于零, 不包括零)
Assert.Zero
用于断言数字类型为数字零
Assert.NotZero
用于断言数字类型不是零.
很多时候, Not 包含的范围非常广, 进行单元测试是为了在开发阶段找出问题, 解决问题, 因此断言的范围越窄越好, 我们不能仅仅让单元测试通过了事.
比如一个方法返回的结果是数字类型, 我们要断定它是正数? 大于某一个数的正数? 在一定范围的正数? 是一个具体的正数? 而不能简单的是零, 不是零. 当然这还要根据业务本身来确实, 有些时候范围可能确实很大, 但是一定要注意单元测试原则.
Assert.Greater(OrEqual)
用于断言数字类型的变量大于 (或者等于) 某一个值
Assert.Less(OrEqual)
用于断言数字类型小于 (或者等于) 某一值
Assert.Contains
用于断言集合中是否包含某一元素.
比如以下方法, 用于断言字符串数组中是否包含特定字符串
Assert.AreSame
用于断言两个对象是否相等
这个静态方法并没有提供重载参数用于指定一个比较器来比较引用对象的相等性, 需要实现 equals 和 gethashcode 方法才能得到预期结果, 但在实际中我们往往把比较器放在类外边, 如何在比较引用对象的时候加载一个比较器在后面章节会有介绍, 这里先略过.
Nunit 测试基础之复杂断言
Nunit 测试基础之复杂断言
上面一篇我们讲解了一些基本断言, 利用这些断言我们就可以进行单元测试了, 然而仅仅使用简单断言还是不够的, 如果逻辑复杂度较高, 使用简单的断言会导致单元测试代码量增加, 最终导致单元测试本身过于复杂和难以维护. 需要说明的是这里所说的复杂断言仍然在 Assert 的静态方法里面, 本身也不是特别复杂, 只是比前面讲的秒复杂一些, 只是如果没有了这些方法, 一些特殊功能实现起来比较费劲基本无法实现.
下面就介绍一下这些方法.
Assert.Catch
Assert.Catch 有泛型和异步方法, 这里只介绍其泛型方法. 很多即使经常使用单元测试功能的人也未必用过这个方法.
其实这个方法和 Assert.Throw 用法上类似, 只是有一点不同的是要测试的方法里的异常可以是 catch 到的异常的子类, 实际开发中, 如果我们能确立异常的类型, 则最好捕获具体类型异常, 然而不能排除有一些不够规范的代码整段代码被一个 try catch 包围, 这时候不一定能够捕获到想要的特定异常, 这时候可以使用 Assert.Catch
以上代码类似上一节中讲 throw 时使用的代码, 只是这里泛型参数里是 Exception 而不是具体的异常信息, 我们运行这段代码, 依然能够测试通过.
在单元测试中, 期待的状态越具体越好, 然而由于种种原因 (比如立项时候没有对代码规范做过多要求, 开发者水平不高, 要测试的代码是别人写的, 写单元测试的人对其中逻辑并不是特别清楚等) 我们无法做到非常具体, 这个时候可以把要获得的状态放宽以后, 待条件完备了再修改单元测试以进一步收窄状态.
Assert.Ignore
Assert.Ignore 和 Ignore 注解功能类似, 可以在测试的时候忽略一个单元测试. 有些情况下我们需要暂时忽略一个测试, 比如说要进行测试的内容有一个外部依赖, 现在外部依赖暂时不可用, 如果我们不忽略的话测试将会失败, 在自动化环境下, 失败将导致无法进行下一步动作, 此时我们可以暂时忽略这个测试.
忽略的测试前面有一个 黄色叹号标志, 警示我们需要注意.
Assert. Fail
我们先看一下面一段代码
在这个单元测试本身使用到了 try catch, 我们知道 WhetherNameContainsB 方法在 Person 类的 Name 没有提供值的情况下会抛出异常, 然而我们的代码并没有断言这个异常存在, 此时由于 catch 代码块存在, 会把异常吞掉, 因此最终我们断言 person 的 Age 为正数的时候将会通过(我们在构造类的时候设置了 Age 为 32)
这显然不行的, 这时候我把们 Assert.Fail(e.Message)取消注释, 测试便会变成失败状态.
Assert.IsNaN
用于断言一个 Double 类型数字是否是 NaN
虽然实际业务中我们并不会写以上代码, 但是如果除数和被除数是通过复杂计算得来的则有可能除数和被除数都是零.
Assert.IsInstanceOf
用于断言一个对象是否是指定类型的实例,
如上 psn 是 Person 类的一个实例, 而 Person 继承自 Object, 因此 psn 也是 Object 类的实例
Assert.IsAssignableFrom
此方法和以上方法作用相反, 它用来断言指定类型是当前对象类型的子类.(Assert.IsInstanceOf 判断的是当前对象是指定类型的子类)
这个方法语义不是很明确, 很容易搞晕, 使用的时候需要特别注意
Assert.Warn
用于使一个测试通过, 但是出现警示信息.
来源: https://www.cnblogs.com/tylerzhou/p/11286438.html