I would like to be able to use JUnit rules such as TemporaryFolder
or other TestRule
s 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
TestRule
s possible?
- If not, how to use JUnit
TestRule
s 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
}
}
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)
}
}
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.
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
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()
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?
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.