在 ScalaTest 中使用 JUnit @Rule(例如 TemporaryFolder)

Using JUnit @Rule with ScalaTest (e.g. TemporaryFolder)

我希望能够使用 JUnit 规则,例如 TemporaryFolder 或我们已经在内部开发的其他 TestRule。 实现该目标的最佳方法是什么?我知道 JUnitSuite,但它似乎没有接受 @Rule 注释。 无论如何,我想使用不同的 ScalaTest 套件。

所以我的问题是:


这是我用 JUnitSuite 尝试的结果:

class MyTest extends JUnitSuite {
  //@Rule
  //val temporaryFolder = new TemporaryFolder() // throws java.lang.Exception: The @Rule 'temporaryFolder' must be public.

  @Rule
  def temporaryFolder = new TemporaryFolder()

  @Test
  def test: Unit = {
    assert(temporaryFolder.newFile() !== null) // java.lang.IllegalStateException: the temporary folder has not yet been created
  }
}

这是我基于 ScalaTest 的 documentation on fixtures 得出的结论。不过还是想知道有没有更好的解决办法

  1. 贷款夹具法

    class LoanFixtureTest extends FunSuite {
      def withRule[T <: TestRule](rule: T)(testCode: T => Any): Unit = {
        rule(
          new Statement() {
            override def evaluate(): Unit = testCode(rule)
          },
          Description.createSuiteDescription("JUnit rule wrapper")
        ).evaluate()
      }
    
      test("my test") {
        withRule(new TemporaryFolder()) { temporaryFolder =>
          assert(temporaryFolder.newFile() !== null)
        }
      }
    }
    
    • 优点:允许仅将规则应用于需要的测试
    • 缺点:用法不是很优雅;当需要多个 TestRules 时很笨拙
  2. 使用具有 withFixture(test: NoArgTest) 覆盖的可堆叠混音

    trait TemporaryFolderFixture1 extends SuiteMixin {
      this: Suite =>
      val temporaryFolder = new TemporaryFolder
    
      abstract override def withFixture(test: NoArgTest) = {
        var outcome: Outcome = null
        val statementBody = () => outcome = super.withFixture(test)
        temporaryFolder(
          new Statement() {
            override def evaluate(): Unit = statementBody()
          },
          Description.createSuiteDescription("JUnit rule wrapper")
        ).evaluate()
        outcome
      }
    }
    
    class StackableTraitFixtureTest extends FunSuite with TemporaryFolderFixture1 {
      test("my test") {
        assert(temporaryFolder.newFile() !== null)
      }
    }
    
    • 优点:使用非常简单,方便地允许在
    • 中混合多个规则
    • 缺点:要求每个规则都有一个 mixin;即使对于不需要规则的测试也需要调用规则;不能使用规则,例如在 BeforeAfterEach#beforeEach()
  3. 覆盖withFixture(test: OneArgTest)

    trait TemporaryFolderFixture2 {
      thisFixture: org.scalatest.fixture.FunSuite =>
      type FixtureParam = TemporaryFolder
    
      override protected def withFixture(test: OneArgTest): Outcome = {
        val temporaryFolder = new TemporaryFolder()
        var outcome: Outcome = null
        temporaryFolder(
          new Statement() {
            override def evaluate(): Unit = {
              outcome = withFixture(test.toNoArgTest(temporaryFolder))
            }
          },
          Description.createSuiteDescription("JUnit rule wrapper")
        ).evaluate()
        outcome
      }
    }
    
    class OneArgWithFixtureTest extends org.scalatest.fixture.FunSuite with TemporaryFolderFixture2 {
      test("my test") { temporaryFolder =>
        assert(temporaryFolder.newFile() !== null)
      }
    }
    
    • 缺点:只允许一个 TestRule,使泛型与任何规则一起工作而不仅仅是 TestRule 需要额外的努力

你最喜欢哪一个?

您可以通过创建类型为 TemporaryFolder 的成员字段并通过 @Rule 函数返回该字段值来解决问题。

class MyTest extends JUnitSuite {

  val _temporaryFolder = new TemporaryFolder

  @Rule
  def temporaryFolder = _temporaryFolder

  @Test
  def test: Unit = {
    assert(temporaryFolder.newFile() !== null)
  }
}

这对我有用。基于 answer。所以注释将应用于 到(合成)getter 方法

import org.junit._
import scala.annotation.meta.getter

class MyTest extends JUnitSuite {

  @(Rule @getter)
  val tempFolder = new TemporaryFolder

}

只要确保使用 junit 版本 >4.11。