如果你还记得,我们在7.4节说过TDD是动态语言的理想用例。实际上,Scala先进的类型推断让它在做测试上同样也有很多优势,尽管它是静态类型系统,还是经常会让人觉得它是动态语言。
Scala中的主测试框架是ScalaTest。它为做各种测试提供了一些极其实用的特质和类——从JUnit风格的单元测试到全面的集成和验收测试。我们来看一个ScalaTest的实战例子。
代码清单11-21用ScalaTest重写了11.4节中的代码,并且加了一个新的sellTicket
方法测试fiftyDiscountTickets
。
代码清单11-21 ScalaTest风格的JUnit测试
import java.math.BigDecimalimport java.lang.IllegalArgumentExceptionimport org.scalatest.junit.JUnitSuiteimport org.scalatest.junit.ShouldMatchersForJUnitimport org.junit.Testimport org.junit.Beforeimport 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: 4RevenueTest:- zeroSalesEqualsZeroRevenue- failIfTooManyOrTooFewTicketsAreSold- tenTicketsSoldIsThreeHundredInRevenue- fiftyDiscountTicketsRun completed in 820 milliseconds.Total number of tests run: 4Suites: completed 1, aborted 0Tests: succeeded 4, failed 0, ignored 0, pending 0All 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.JUnitCorecom.java7developer.chapter11.scalatest.RevenueTestJUnit version 4.8.2...Time: 0.096OK (4 tests)
当然,输出会稍有不同,因为执行测试用的是不同的工具(JUnit运行器)。
注意 在用Maven构建第12章的java7developer项目时,我们会用这个JUnit运行器。
用ScalaTest测试Scala代码
我们在这一节主要讨论用ScalaTest测试Java代码。但如果你用Scala作为项目中的主要编程语言会怎么样?
人们通常认为Scala是稳定层语言,所以如果你在使用Scala代码,应该也可以像测试Java代码那样测试Scala代码库。所以用ScalaTest代替JUnit是使用TDD方式的不二之选。
快速了解ScalaTest后,我们对TDD的讨论就结束了。