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

15

我希望能够在测试中使用JUnit规则,例如TemporaryFolder或其他已经在内部开发的TestRule。 如何最好地实现这一目标?我知道有JUnitSuite,但它似乎无法识别@Rule注释。 无论如何,我想使用不同的ScalaTest套件。

因此,我的问题是:

  • ScalaTest套件是否支持JUnit规则?
  • 如果不支持,是否存在库可以使用JUnit的TestRule
  • 如果没有,如何在Scala测试中使用JUnit的TestRule
  • 还是有更适合的Scala特定方法来实现TemporaryFolder,或者例如Stefan Birkner的System Rules所提供的功能?

以下是我尝试使用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
  }
}
3个回答

15

你可以通过创建一个类型为TemporaryFolder的成员变量,并通过@Rule函数返回该成员变量值来解决问题。

class MyTest extends JUnitSuite {

  val _temporaryFolder = new TemporaryFolder

  @Rule
  def temporaryFolder = _temporaryFolder

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

啊,谢谢,那个可行 :). 没有创建多个 TemporaryFolder 实例。最终,我们采用了可堆叠的 mixins 解决方案,这样我们就不会被 JUnitSuite 锁定。但无论如何,很高兴知道 @Rule 可以在 scalatest 中得到支持。 - Mifeet
很高兴我能帮到你 :-) - Till Rohrmann

11

我基于ScalaTest的fixture文档得出以下方案,但我想知道是否有更好的解决方案。

  1. Loan-fixture method

    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)
        }
      }
    }
    
    • Pros: allows applying the rule only to tests where it is needed
    • Cons: not very elegant usage; clumsy when multiple TestRules are required
  2. Using stackable mixins with withFixture(test: NoArgTest) override

    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)
      }
    }
    
    • Pros: very simple usage, conveniently allows mixing multiple rules in
    • Cons: requires having a mixin for every rule; rules need to be invoked even for tests that don't need them; rule cannot be used e.g. in BeforeAfterEach#beforeEach()
  3. Overriding 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)
      }
    }
    
    • Cons: allows only one TestRule, making in generic to work with any rule instead of just TestRule would require an extra effort

你最喜欢哪一个?


9
这对我有帮助。基于答案。所以注释将应用于(合成的)getter方法。
import org.junit._
import scala.annotation.meta.getter

class MyTest extends JUnitSuite {

  @(Rule @getter)
  val tempFolder = new TemporaryFolder

}

请确保使用junit版本>4.11。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接