Clojure世界:单元测试

简介:
    单元测试也是一个开发中最常见的需求,在Java里我们用JUnit或者TestNG,在clojure里也内置了单元测试的库。标准库的 clojure.test,以及第三方框架 midje。这里我将主要介绍clojure.test这个标准库,midje是个更加强大的测试框架,广告下,midje的介绍在第二次cn-clojure聚会上将有个Topic,我就不画蛇添足了。通常来说,clojure.test足够让你对付日常的测试。

    首先看一个最简单的例子,定义一个函数square来计算平方,然后我们测试这个函数:
;;引用clojure.test
(ns example
  (:use [clojure.test :only [deftest 
is  run - tests]]))
;;定义函数
(defn square [x]
  (
*  x x))
;;测试函数
(deftest test
- square
  (
is  ( =   4  (square  2 )))
  (
is  ( =   9  (square  - 3 ))))
;;运行测试
(run
- tests  ' example)

    执行输出:

Testing example

Ran 
1  tests containing  2  assertions.
0 failures, 0 errors.

    这个小例子基本说明了clojure.test的主要功能。 首先是断言is,类似JUnit里的assertTrue,用来判断form是否为true,它还可以接受一个额外的msg参数来描述断言:
 ( is  ( =   4  (square  2 ))  " a test " )
    它还有两种变形,专门用来判断测试是否抛出异常:
 ( is  (thrown? RuntimeException (square  " a " )))
 (
is  (thrown - with - msg? RuntimeException  # "java.lang.String cannot be cast to java.lang.Number"  (square "a")))
    上面的例子故意求"a"的平方,这会抛出一个java.lang.ClassCastException,一个运行时异常,并且异常信息为java.lang.String cannot be cast to java.lang.Number。我们可以通过上面的方式来测试这种意外情况。clojure.test还提供了另一个 断言are,用来判断多个form:
 (testing  " test zero or one "
    (are
     (
=  0 (square 0))
     (
=   1  (square  1 ))))
    are接受多个form并判断是否正确。这里还用了testing这个宏来添加一段字符串来描述测试的内容。

    其次,我们用deftest宏定义了一个测试用例,deftest定义的测试用例也可以组合起来:
   (deftest addition
     (
is  ( =   4  ( +   2   2 )))
     (
is  ( =   7  ( +   3   4 ))))
   (deftest subtraction
     (
is  ( =   1  ( -   4   3 )))
     (
is  ( =   3  ( -   7   4 ))))
   (deftest arithmetic
     (addition)
     (subtraction))

    但是组合后的tests运行就不能简单地传入一个ns,而需要定义一个test-ns-hook指定要跑的测试用例,否则组合的用例如上面的addition和subtraction会运行两次。我们马上谈到。

    定义完用例后是运行测试,运行测试使用run-tests,可以指定要跑测试的ns,run-tests接受可变参数个的ns。刚才提到,组合tests的时候会有重复运行的问题,要防止重复运行,可以定义一个 test-ns-hook的函数:
(defn test - ns - hook []
  (test
- square)
  (arithmetic))
    这样run-tests就会调用test-ns-hook按照给定的顺序执行指定的用例,避免了重复执行。

    在你的测试代码里明确调用run-tests执行测试是一种方式,不过我们在开发中更经常使用的是 lein来管理project, lein会将src和test分开,将你的测试代码组织在专门的test目录,类似使用maven的时候我们将main和test分开一样。这时候就可以简单地调用:
        lein test
命令来执行单元测试,而不需要明确地在测试代码里调用run-tests并指定ns。更实用的使用例子可以看一些开源项目的组织。

    单元测试里做mock也是比较常见的需求,在clojure里做mock很容易,原来clojure.contrib有个mock库,基本的原理都是利用binding来动态改变被mock对象的功能,但是在clojure 1.3里,binding只能改变标注为dynamic的变量,并且clojure.contrib被废弃,部分被合并到core里面,Allen Rohner编译了一个可以用于clojure 1.3的clojure.contrib,不过需要你自己install到本地仓库,具体看 这里。不过clojure.contrib.mock哪怕使用1.2的编译版本其实也是可以的。

    clojure.contrib最重要的是expect宏,它类似EasyMock里的expect方法,看一个例子:
(use [clojure.contrib.mock :only [times returns has - args expect]])

(deftest test
- square2
  (expect [square (has
- args [number?] (times  2  (returns  9 )))]
          (
is  ( =   9  (square  4 )))))

    has-args用来检测square的参数是不是number,times用来指定预期调用的次数,而returns用来返回mock值,是不是很像EasyMock?因为我们这个测试只调用了square一次,所以这个用例将失败:
Testing example
" Unexpected invocation count. Function name: square expected: 2 actual: 1 "
   这个例子要在Clojure 1.3里运行,需要将square定义成dynamic:
(defn  ^ :dynamic square [x]
  (
*  x x))
   否则会告诉你没办法绑定square:
actual: java.lang.IllegalStateException: Can ' t dynamically bind non-dynamic var: example/square



    额外提下,还有个轻量级的测试框架expections可以看一下,类似Ruby Facker的facker库提供一些常见的模拟数据,如名称地址等

文章转自庄周梦蝶  ,原文发布时间2012-02-15

目录
相关文章
|
2月前
|
测试技术 持续交付
单元测试模块化编程
单元测试模块化编程
18 1
|
3月前
|
测试技术 Python
Python中的单元测试与测试驱动开发(TDD)实践
Python中的单元测试与测试驱动开发(TDD)实践
|
4月前
|
测试技术 Python
Python自动化测试与单元测试框架
现在越来越多的软件开发人员开始采用Python进行自动化测试和单元测试。Python具有简单易学、灵活性高等特点,加上其丰富的测试框架,使得Python自动化测试和单元测试成为了开发人员不可或缺的一部分。本文将介绍Python自动化测试和单元测试框架的基础知识和使用方法。
|
6月前
|
测试技术 开发者 Python
Python中的单元测试与测试驱动开发(TDD)指南
测试是保障代码质量的关键步骤,而Python提供了强大的测试工具和框架,让开发者能够轻松地进行单元测试和测试驱动开发(TDD)。本文将深入探讨单元测试的概念、`unittest`库的基本用法,以及如何通过TDD的方式构建高质量的Python代码。
|
11月前
|
测试技术 Linux C++
gtest单元测试框架介绍及简单使用
gtest单元测试框架介绍及简单使用
|
测试技术 Python
Testify Pythoinc的单元测试框架
Testify 是Python的一款测试框架,可以替代unittest和nose。
141 0
Testify Pythoinc的单元测试框架
|
安全 Java 测试技术
Java单元测试之 单元测试规范
对于程序员是否有必要编写test case,何时编写依然存在很多争议,各种互斥的方法论(SE/AM/XP/TDD),以及不同的开发文化,但是可以确定是编写单元测试用例有助于提高编程能力。
8108 2
Java单元测试之 单元测试规范
|
缓存 Java 测试技术
Spock单元测试框架初探
  软件工程发生在代码被非原作者阅读之时   Spock vs JUnit 单元测试框架,JUnit读者已了解,因此直接开门见山,基于JUnit和Spock做一个对比,明显Spock在工程化更有优势。 对比项 Spock JUnit 结构可读性 ✓   问题定位效率 ✓  
956 0