12.9 用JUnit运行ScalaTest

至此,想必你已然爱上了ScalaTest,但很快就又会意识到,项目里面绝大多数测试都是用JUnit或TestNG写的。你很困惑,能不能既用上ScalaTest那简洁的语法和众多特性,还可以用JUnit或TestNG运行测试呢?JUnit3SuiteTestNGSuite就可以做到这一点。你只需要让测试套件继承JUnit3Suite,JUnit就可以识别出来了,测试方法自然还是按照你的爱好去写:你可以用ScalaTest的assert()expect()intercept(),还有上面提到的用函数式风格共享代码。这样写成的测试,不管是ScalaTest还是JUnit都可以运行。不过它只支持JUnit 3.x(在JUnit 3.8.1上测试过),不支持JUnit 4.0⑪。下面是JUnit3Suite的使用示例:

⑪ScalaTest 1.0 支持JUnitSuite,用于JUnit 4.x。——译者注


UnitTestingWithScala/UsingJUnit3Suite.scala

  1. class UsingJUnit3Suite extends org.scalatest.junit.JUnit3Suite {
  2. def withList(testFunction : (java.util.ArrayList[Integer]) => Unit) {
  3. val list = new java.util.ArrayList[Integer]
  4. try {
  5. testFunction(list)
  6. }
  7. finally {
  8. // perform any necessary cleanup here after return
  9. }
  10. }
  11. def testListEmptyOnCreate() {
  12. withList { list => expect(0, "Expected size to be 0") { list.size() } }
  13. }
  14. def testGetOnEmptyList() {
  15. withList {
  16. list => intercept[IndexOutOfBoundsException] { list.get(0) }
  17. }
  18. }
  19. }

下面是用JUnit运行上述测试的Scala代码:

UnitTestingWithScala/RunJUnitTest.scala

  1. object RunJUnitTest {
  2. def main(args: Array[String]) =
  3. junit.textui.TestRunner.run(classOf[UsingJUnit3Suite])
  4. }

上段代码编译后,用ScalaTest和JUnit都能运行。

接下来你可以看到怎么进行编译,然后分别用两种工具运行:

  1. scalac -classpath $SCALATEST:$JUNITJAR:. \
  2. UsingJUnit3Suite.scala RunJUnitTest.scala
  3. echo "Running ScalaTest"
  4. scala -classpath $SCALATEST:$JUNITJAR:. \
  5. org.scalatest.tools.Runner -o -p . -s UsingJUnit3Suite
  6. echo "Running JUNIT test"
  7. java -classpath $SCALALIBRARY:$SCALATEST:$JUNITJAR:. RunJUnitTest

测试代码的输出如下:

  1. Running ScalaTest
  2. Run starting. Expected test count is: 2
  3. Suite Starting - UsingJUnit3Suite: The execute method of a nested suite
  4. is about to be invoked.
  5. Suite Starting - UsingJUnit3Suite: UsingJUnit3Suite
  6. Test Starting - testGetOnEmptyList: UsingJUnit3Suite
  7. Test Succeeded - testGetOnEmptyList: UsingJUnit3Suite
  8. Test Starting - testListEmptyOnCreate: UsingJUnit3Suite
  9. Test Succeeded - testListEmptyOnCreate: UsingJUnit3Suite
  10. Suite Completed - UsingJUnit3Suite: UsingJUnit3Suite
  11. Suite Completed - UsingJUnit3Suite: The execute method of a nested
  12. suite returned normally.
  13. Run completed. Total number of tests run was: 2
  14. All tests passed.
  15. Running JUNIT test
  16. ..
  17. Time: 0.02
  18. OK (2 tests)

在上面的例子中,你看到了怎么用Scala和JUnit来运行ScalaTest写的测试。这个特性降低了把Scala引入当前项目编写单元测试的门槛。现在就用不着在JUnit(或TestNG)和ScalaTest之间二选一了。二者可以相得益彰,既用上Scala的简洁,又保留着项目中现有的完好框架。

编写单元测试时,人们常常要依赖mock对象来打桩,或是模拟被测代码所依赖的代码。如果你用过EasyMock或是JMock之类的框架创建mock对象,也同样可以在Scala里使用它们。使用这些Mock框架时,可以充分利用Scala的诸多特性,如trait、函数式风格和简洁。

在这章里,你已经学到了很重要的工具和实践。不过,你或许也该明白,单元测试并不仅仅是这些工具而已,它还需要个人的修炼和努力。我衷心希望Scala在测试上表现出来的优雅和ScalaTest所提供的便利,能够激励你开始编写测试,或是把测试继续写好。在下一章里,我们共同探索Scala的异常处理与Java的异同。