名著阅读 > Java程序员修炼之道 > 11.3 ScalaTest >

11.3 ScalaTest

如果你还记得,我们在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的讨论就结束了。