如果你還記得,我們在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的討論就結束了。