ScalaTest 是一个开源的Java,Scala 的测试框架,它整合了JUnit, TestNG, Ant, and Maven 使你能非常高效,同时又符合你自己需要的单元测试方式。
API :http://www.scalatest.org/scaladoc/doc-1.2/
首先看一下ScalaTest 本身提供了哪些测试基类或者特质:
1 )org.scalatest.Suite
只要把测试类扩展Suite ,并且定义了名字以“test ”开头的测试方法,就可以实现简单的测试,参考下面的代码:
import org.scalatest.Suite class MySuite extends Suite{ def testOk(){ assert(true) } def testOther(){ assert(true) } }
testOk() 函数就是为了测试MySuite 这个测试类是否能够跑通,如果想看看测试效果,你可以写个object 中的main 方法中new 一个MySuite 对象调用execute() 方法,可以测试所有方法,像这样
(new MySuite).execute()
这是控制台会打印测试结果,
Test Starting - MySuite: testOk
Test Succeeded - MySuite: testOk
Test Starting - MySuite: testOther
Test Succeeded - MySuite: testOther
这说明测试成功了。execute() 方法还提供了一个String 参数,传入方法名,作用就是测试特定的方法( 假设MySuite 中含有2 个以上的方法) ,像这样
(new MySuite).execute("testOk")
ps :在这里会发生版本冲突,如果你的scala.**.jar 的版本是2.8 ,那么scalatest 版本最好是1.2 。经测试,如果不是这样,yoursuite.execute() 方法会失效报异常,必须传入具体方法名才有效。
2 )org.scalatest.FunSuite (extends Suite )
ScalaTest 提供了名为 FunSuite 的特质,重载了execute ,从而可以让你以函数值的方式而不是方法定义测试:
import org.scalatest.FunSuite class MyFunSuite extends FunSuite{ test("if ok"){ assert(true) } }
FunSuite 的一个好处是你不用给你所有的测试以“test ”开头起名。而且,你可以更容易地给你的测试起很长的名称,因为你不需要把它们编码为驼峰形式。
下面我们再来看看scalatest 提供了哪些断言方法。
1 )assert()
第二节中已经出现这个断言,单参数的assert(condition 表达式) 已经了解,完全类似于JUnit 的assertEqauls(value1,value2) 方法,不过scalatest 为assert() 方法又重载了双参数的方法,其作用就是自定义报错提示,
assert(condition 表达式,“报错的文本提示”)
如果在condition 表达式中运用“=== ”,那么其作用相当于assert() 双参数方法,但是报错提示是缺省格式的,像这样3 did not equal 2
2 )except()
except() 方法的结构是这样的,except( 期望值){ 比较值} ,报错信息像这样Expected 2, but got 3 ,同样scalatest 为except 提供了类似assert 第二参数的作用,
except( 期望值,“报错的文本提示”){ 比较值}
3 )intercept()
其作用检查方法是否抛出了期待的异常,其结构是,
intercept(classOf[ 异常类]){ 会抛异常的表达式}
和
intercept(classOf[ 异常类] ,“自定义报错信息”){ 会抛异常的表达式}
如果代码没有抛出异常,或抛出了不同的异常,intercept 将抛出 AssertionError 前者的缺省报错则类似这样,Expected IllegalArgumentException to be thrown, but NegativeArraySizeException was thrown.
4)org.scalatest.matchers.ShouldMatchers 和 org.scalatest.matchers.MustMatchers
如果你想使用更丰富的更个性的断言表达式,可以在你的单元测试类里面混入这两个匹配器
他们的表达方式是这样的:
object should equal (3) 和 object must equal (3)
他们还有很多表达式这里就不一一列举了详见API 。
如果你想知道他们有什么区别,可以告诉你们这俩个类的实现方式是一样的,只是语气上是
should 和must 的区别,作用也是一样的,你比较顺眼哪个就混入哪个。
1)org.scalatest.tools.Runner 运行
ScalaTest 的Runner 应用可以在命令行或ant 任务中调用。前提条件是被测试对象必须是Suite 测试集。Runner 工具运行必须指定要运行的测试集,方式包括显式说明测试集名称或者说明你想要Runner 执行自动发现的测试集名称前缀。你可以选择性地指定运行路径:runpath ,目录列表和装载测试类的JAR 文件以及它们测试的代码。你还可以指定一个或多个报表器,以格式化测试结果的外观。例如,ScalaTest 的发布包里包含了测试ScalaTest 自身的测试集。你可以使用下列命令执行其中的测试集,SuiteSuite :
我在本机进入scalatest-1.2 的安装目录下,输入
>scala – cp scalatest-1.2.jar org.scalatest.tools.Runner
-p "scalatest-1.2-tests.jar" – s org.scalatest.SuiteSuite
就可以运行SuiteSuite 这个测试集了,还会有工具自带的GUI 窗口显示:
-cp 的目的是把 ScalaTest 的 JAR 文件放在类路径中。下一项,org.scalatest.tools.Runner ,是 Runner 应用的全称。Scala 将运行这个应用并把剩余的项当做命令行参数传递给应用。-p 指定了运行路径,这里是包含了测试集类的 JAR 文件:scalatest-1.2-tests.jar 。-s 说明了SuiteSuite 是要执行的测试集。
如果每一个Suite 测试集都要输一个命令行来执行的话,必然是相当麻烦,下面就来看看在ant 任务中配置Runner 运行脚本是怎么样的:
最主要的就是配置ant 的build.xml ,写好这个配置文件,Eclipse 想要运行build 任务,就只需调用ant 工具就可以了。这个是build.xml 的主要配置代码:引入了scalatest 提供的
org.scalatest.tools.ScalaTestAntTask 标签库。
<?xml version="1.0" encoding="GB2312"?> <project name="scala_ceshi" default="test" basedir="."> <property name="scala.home" value="C:\Program Files\scala-2.8.0"/> <property name="src.dir" value="src"/> <property name="test.dir" value="test"/> <property name="build.dir" value="build"/> <property name="build.test" value="${build.dir}/test" /> <property name="build.classes" value="${build.dir}/classes" /> <property name="scala-library.jar" value="${scala.home}/lib/scala-library.jar"/> <target name="clean" description="Removes previous build"> <delete quiet="true" verbose="false" dir="${build.dir}"/> </target> <target name="make_dir" depends="clean"> <mkdir dir="${build.classes}"/> <mkdir dir="${build.test}"/> </target> <target name="init_classpaths" depends="make_dir"> <path id="classpath.build"> <pathelement location="${scala-library.jar}"/> </path> <path id="classpath.test"> <pathelement location="${build.classes}" /> <pathelement location="C:\apache-maven-2.2.1\MyRepository\org\scalatest\scalatest\1.0.1-for-scala-2.8.0.Beta1-NQRFPT\scalatest-1.0.1-for-scala-2.8.0.Beta1-NQRFPT.jar" /> <pathelement location="${scala-library.jar}"/> </path> </target> <target name="init_ant_tasks" depends="init_classpaths"> <taskdef resource="scala/tools/ant/antlib.xml"> <classpath> <pathelement location="${scala.home}/lib/scala-compiler.jar"/> <pathelement location="${scala-library.jar}"/> </classpath> </taskdef> <taskdef name="scalatest" classname="org.scalatest.tools.ScalaTestAntTask"> <classpath refid="classpath.test"/> </taskdef> </target> <target name="compile" depends="init_ant_tasks"> <scalac srcdir="${src.dir}" destdir="${build.classes}" classpathref="classpath.build" deprecation="on" force="changed"> </scalac> </target> <target name="test" depends="compile"> <scalac srcdir="${test.dir}" destdir="${build.test}" classpathref="classpath.test" force="changed"> </scalac> <scalatest> <runpath> <pathelement location="${build.test}"/> </runpath> <reporter type="stdout"/> <suite classname="YourSuiteTestName"/> </scalatest> </target> </project>
这样就可以用<scalatest> 标签配置N 个Suite 测试集了。调用ant-build 既可以测试相关的单元测试。
2 )使用JUnit 运行
JUnit 运行scalatest 的Suite 测试集的方式也很多:
这里就说由scalatest 自己提供的类库实现运行,
首先是org.scalatest.junit.JUnit3Suite 和org.scalatest.junit.JUnitSuite
只要你的测试集扩展了这两个类中一个类,你就可以用相应的JUnit3 或JUnit4 工具运行这个测试集,该测试集中可以使用Suite 所有断言语法,当然你的测试集也可以用scalatest 的Runner 工具运行。如果就想把测试集当成一个只用JUnit 运行的测试的话,那么你的测试集混入org.scalatest.junit.AssertionsForJunit 特质即可。
使用这种混入JUnit 特质的方式时,必须要在测试方法前加入org.junit.Test 标签,这样
JUnit 才能识别该方法的有效性。
=================================================================
接下来一种方法就是org.junit.runner.RunWith 和 org.scalatest.junit.JUnitRunner 的组合使用:
import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner import org.scalatest.Suite @RunWith(classOf[JUnitRunner]) class MySuiteTest extends Suite { def testAddition() { val sum = 1 + 1 assert(sum === 2) assert(sum + 2 === 4) } def testSubtraction() { val diff = 4 - 1 assert(diff === 3) assert(diff - 2 === 1) } }
这样,JUnit 工具也可以运行这个Suite 测试集。