如果你还记得,我们在7.4节说过TDD是动态语言的理想用例。实际上,Scala先进的类型推断让它在做测试上同样也有很多优势,尽管它是静态类型系统,还是经常会让人觉得它是动态语言。
Scala中的主测试框架是ScalaTest。它为做各种测试提供了一些极其实用的特质和类——从JUnit风格的单元测试到全面的集成和验收测试。我们来看一个ScalaTest的实战例子。
代码清单11-21用ScalaTest重写了11.4节中的代码,并且加了一个新的sellTicket
方法测试fiftyDiscountTickets
。
代码清单11-21 ScalaTest风格的JUnit测试
import java.math.BigDecimal
import java.lang.IllegalArgumentException
import org.scalatest.junit.JUnitSuite
import org.scalatest.junit.ShouldMatchersForJUnit
import org.junit.Test
import org.junit.Before
import org.junit.Assert._
class RevenueTest extends JUnitSuite with ShouldMatchersForJUnit {
var venueRevenue: TicketRevenue = _
@Before def initialize {
venueRevenue = new TicketRevenue
}
@Test def zeroSalesEqualsZeroRevenue {
assertEquals(BigDecimal.ZERO, venueRevenue estimateTotalRevenue 0);
}
@Test def failIfTooManyOrTooFewTicketsAreSold {
evaluating { venueRevenue.estimateTotalRevenue(-1) }
➥ should produce [IllegalArgumentException] //预期的异常
evaluating { venueRevenue.estimateTotalRevenue(101) }
➥ should produce [IllegalArgumentException]
}
@Test def tenTicketsSoldIsThreeHundredInRevenue {
val expected = new BigDecimal("300");
assert(expected == venueRevenue.estimateTotalRevenue(10));
}
@Test def fiftyDiscountTickets {
for (i <- 1 to 50)
➥ venueRevenue.sellTicket(new Ticket)
for (i <- 1 to 50)
➥ venueRevenue.sellTicket(new Ticket(new StubPrice,
➥ new BigDecimal(0.9)))
assert(1950.0 ==
➥ venueRevenue.getRevenue.doubleValue);//Scala风格的断言
}
}
我们还没讲过Scala如何处理注解。它们看起来跟Java注解一样。这没什么好说的。你的测试也是放在扩展了JUnitSuit
的类中,这就是说ScalaTest会把这个类当做它能运行的东西。
你可以在命令行中用本地ScalaTest运行器轻松运行ScalaTest:
ariel:scalatest boxcat$ scala -cp /Users/boxcat/projects/tickets.jar:/Users/
boxcat/projects/wgjd/code/lib/scalatest-1.6.1.jar:/Users/boxcat/
projects/wgjd/code/lib/junit-4.8.2.jar org.scalatest.tools.Runner -o -s
com.java7developer.chapter11.scalatest.RevenueTest
在这条命令中,所测试的Java类放在tickets.jar文件中,所以要把它跟ScalaTest和JUnit文件一起放在类路径中。
这条命令用-s
选项指定了要运行的测试集(省略-s
选项会运行所有测试集中的所有测试)。-o
选项把测试输出发送到标准输出中(用-e
把测试结果输出到标准错误流中)。ScalaTest参照这个配置输出报道途径(包括其他途径,比如图形化界面)。前面的例子产生的输出如下所示:
Run starting. Expected test count is: 4
RevenueTest:
- zeroSalesEqualsZeroRevenue
- failIfTooManyOrTooFewTicketsAreSold
- tenTicketsSoldIsThreeHundredInRevenue
- fiftyDiscountTickets
Run completed in 820 milliseconds.
Total number of tests run: 4
Suites: completed 1, aborted 0
Tests: succeeded 4, failed 0, ignored 0, pending 0
All tests passed.
这些测试已经被编译进了一个类文件中。只要类路径中有JUnit和ScalaTest两者的JAR,就可以用scala
运行这些测试,而不用在JUnit运行器中。
ariel:scalatest boxcat$ scala -cp /Users/boxcat/projects/tickets.jar:/Users/
boxcat/projects/wgjd/code/lib/scalatest-1.6.1.jar:/Users/boxcat/
projects/wgjd/code/lib/junit-4.8.2.jar org.junit.runner.JUnitCore
com.java7developer.chapter11.scalatest.RevenueTest
JUnit version 4.8.2
...
Time: 0.096
OK (4 tests)
当然,输出会稍有不同,因为执行测试用的是不同的工具(JUnit运行器)。
注意 在用Maven构建第12章的java7developer项目时,我们会用这个JUnit运行器。
用ScalaTest测试Scala代码
我们在这一节主要讨论用ScalaTest测试Java代码。但如果你用Scala作为项目中的主要编程语言会怎么样?
人们通常认为Scala是稳定层语言,所以如果你在使用Scala代码,应该也可以像测试Java代码那样测试Scala代码库。所以用ScalaTest代替JUnit是使用TDD方式的不二之选。
快速了解ScalaTest后,我们对TDD的讨论就结束了。