CLisp 26:测试框架fiveam

regression-test,别名rtestrt,基本APIdef-testrem-testdo-testdo-tests。整个框架只有一个文件,2百多行,不依赖其他工程,非常轻便。用列表保存所有的测试用例,不支持用例分组。支持增量测试,即只执行失败的用例。

 

       fiveam,强大的测试框架,支持很多特性,输出的结果更直观。依赖alexandria工程。

       【加载过程】

       (require ‘asdf)

(load “alexandria.asd”)

       (load “fiveam.asd”)

       (asdf:load :fiveam)

      

       【测试用例】

       定义简单的测试用例,测试1+1=2。宏test用于定义测试用例,第一个参数add-1是用例名称,后面是语句列表,宏is检查输入的表达式是否为真。

(fiveam:test add-1

(fiveam:is (= (+ 1 1) 2)))      

接下来运行这个用例:(fiveam:run!) 或者 (fiveam:run ‘add-1)。有三种执行结果:成功、失败、跳过。用fiveam:run!执行用例后,以检查点为单位进行统计,而不是以用例为单位。执行过程会显示进度,每执行一个检查点输出一个字符,执行成功输出小圆点,失败则输出其它字符。一个用例中可以做多个检查,例如,检查1+1=2 并且 1+2=3

(fiveam:test add-2

(fiveam:is (= (+ 1 1) 2))

(fiveam:is (= (+ 1 2) 3)))

       每次都要写fiveam:is应该很烦人,解决的办法是把宏fiveam:is导入到当前的package中,有很多种方式。执行 (import ‘(fiveam:is) ‘用例所在的包名) 即可,fiveam专门包装了一个函数(fiveam:import-testing-symbols ‘用例所在的包名)。如果定义用例时忘记了,执行时报错找不到函数is,可以用(fiveam:rem-test ‘用例名)删除用例,然后重新定义。

      

       【测试套】

       测试套,就是对用例进行分组。测试套与用例的关系,不像文件夹和文件。所有用例、测试套都不能重名,因为fiveam用一个哈希表fiveam::*test*保存所有的用例和测试套,Key为它们的名称。一个测试套也可以属于多个测试套。测试套和用例一样,都是可以执行的,在实现中两者都是testable-object的子类。下面定义几个测试套,A包含BC

       (fiveam:def-suite a)

       (fiveam:def-suite b :in a)

       (fiveam:def-suite c :in a)

       没有很好的方法查看测试套的包含关系,有需要的话,可以执行下面语句,显示一个测试套包含的子对象。(fiveam::tests (fiveam:get-test 'a))

       接下来在测试套中定义用例,执行用例时需要指定测试套名称,会执行测试套中所有的用例,包括下级测试套。如果不指定测试套名称,则执行不了这些用例。

(fiveam:test (list-1 :suite a)  (is (eql 1 1)))

(fiveam:test (list-2 :suite b)  (is (eql 2 2)))

(fiveam:test (list-3 :suite b)  (is (eql 3 3)))

       系统有一个匿名测试套,没有名称,或者说名称为Nil。如果定义用例时不指定测试套,就会放到匿名套中。如果定义测试套时没有指定父测试套,按理也应该放到匿名套中,但fiveam的实现(文件src/suite.lisp中的函数make-suite)有点问题,没有放进去,导致执行(fiveam:run!)时不能执行指定名称的测试套中的用例。修改make-suite函数,增加下面一句即可解决问题,如果没有指定父测试套,并且已经存在匿名测试套,则将新建的测试套加到匿名测试套中。

       (unless in

      (when-let ((groot (get-test nil)))

        (setf (gethash name (tests groot)) suite)))

       每次定义用例都要指定测试套,有点麻烦。可以用宏fiveam:def-suite*来创建测试套。或者执行宏fiveam:in-suite设置默认的测试套。

 

       【执行时所在的package

       如果在package A中定义用例,在package B中执行用例,用例中使用的A中的函数,还是B中的函数?答案是A中的函数。fiveam框架在定义用例时保存了*package*变量,而在执行用例时恢复了*package*变量。

(make-instance 'test-case

     :name ',name

     :runtime-package (find-package ,(package-name *package*))  定义时保存*package*变量

     ...)

(let ((*readtable* (copy-readtable))

    (*package* (runtime-package test)))    执行时恢复*package*变量

      (funcall (test-lambda test))

      ...)

      

       【依赖关系】

       可以根据其他用例的执行结果,决定是否执行某个用例,称为依赖关系。可以依赖一个用例,也可以依赖多个用例,还可以用andornot组成复杂的条件。执行一个用例时,会自动执行所依赖的用例。例如:

       (fiveam:test (list-4 :depends-on (and list-1 list-4 list-3))  (is (eql 4 4)))

       用例可以依赖测试套,但测试套不能依赖用例或测试套。

 

       Test Fixture测试环境、测试上下文】

       运行测试用例时,经常要构建一个环境,例如准备数据库连接、模拟http请求等等,然后才能执行具体的测试。这些准备工作,对很多测试用例都是一样的,应该避免写重复的代码,可以用宏。

       fiveam提供了一个方法,管理这些构建测试环境的宏,这个特性被称为fixturefixture是可以带输入参数的。def-fixture用于定义fixturerem-fixture用于删除fixture。定义用例时可以指定fixture和输入参数,例如(fiveam:test (test-name :fixture fixture-name) ...)(fiveam:test (test-name :fixture (fixture-name arg1 arg2)) ...)

       例如:

       (fiveam:def-fixture fix-1 (x y)

              (let ((z (* x y)))     (&body)))    这里的&body会被测试用例的body替换掉

       (fiveam:test (fix-1.1 :fixture (fix-1 3 4))

              (is (eql z 12)))

 

       【执行用例】

       (fiveam:run!) 执行所有用例

       (fiveam:run! 用例名或测试套名)

       (fiveam:!)   重复上一次执行过的命令

       (fiveam:!!)      重复上上次执行过的命令

       (fiveam:!!!)     重复上三次执行过的命令

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值