Using JUnit @Rule with ScalaTest (e.g. TemporaryFo

2020-04-02 07:48发布

I would like to be able to use JUnit rules such as TemporaryFolder or other TestRules we have already developed in-house. What is the best method to accomplish that? I'm aware of JUnitSuite but it doesn't seem to pick up the @Rule annotation. I would like to use a different ScalaTest suite anyway.

So my questions are:

  • Are JUnit rules supported by a ScalaTest suit?
  • If not, is there a library out there which would make using Junit TestRules possible?
  • If not, how to use JUnit TestRules in Scala tests?
  • Or is there a more appropriate Scala-specific approach for acomplishing what TemporaryFolder, or, e.g., Stefan Birkner's System Rules provide?

Here's what I tried with 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条回答
小情绪 Triste *
2楼-- · 2020-04-02 07:57

Here is what I came up based on ScalaTest's documentation on fixtures. Still, I would like to know if there is a better solution.

  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

Which one do you like the best?

查看更多
\"骚年 ilove
3楼-- · 2020-04-02 08:04

You could solve the problem by creating a member field of type TemporaryFolder and returning this field value by the @Rule function.

class MyTest extends JUnitSuite {

  val _temporaryFolder = new TemporaryFolder

  @Rule
  def temporaryFolder = _temporaryFolder

  @Test
  def test: Unit = {
    assert(temporaryFolder.newFile() !== null)
  }
}
查看更多
在下西门庆
4楼-- · 2020-04-02 08:20

This worked for me. Based on answer. So annotation will be applied to to the (synthetic) getter method

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

class MyTest extends JUnitSuite {

  @(Rule @getter)
  val tempFolder = new TemporaryFolder

}

Just make sure to use junit version >4.11.

查看更多
登录 后发表回答