How can I use JUnit ExpectedException in Scala?

2019-04-29 03:40发布

问题:

I'd like to be able to use JUnit 4.7's ExpectedException @Rule in Scala. However, it doesn't seem to catch anything:

import org.junit._

class ExceptionsHappen {

  @Rule 
  def thrown = rules.ExpectedException.none

  @Test
  def badInt: Unit = {
    thrown.expect(classOf[NumberFormatException])
    Integer.parseInt("one")
  }
}

This still fails with a NumberFormatException.

回答1:

EDIT: Following the release of JUnit 4.11, you can now annotate a method with @Rule.

You will use it like:

private TemporaryFolder folder = new TemporaryFolder();

@Rule
public TemporaryFolder getFolder() {
    return folder;
}

For earlier versions of JUnit, see the answer below.

--

No, you can't use this directly from Scala. The field needs to be public and non-static. From org.junit.Rule:

public @interface Rule: Annotates fields that contain rules. Such a field must be public, not static, and a subtype of TestRule.

You cannot declare a public fields in Scala. All fields are private, and made accessible by accessors. See the answer to this question.

As well as this, there is already an enhancement request for junit (still Open):

Extend rules to support @Rule public MethodRule someRule() { return new SomeRule(); }

The other option is that it non-public fields be allowed, but this has already been rejected: Allow @Rule annotation on non-public fields.

So your options are:

  1. clone junit, and implement the first suggestion, the method, and submit a pull request
  2. Extend the Scala class from a java class which implements the @Rule

-

public class ExpectedExceptionTest {
    @Rule
    public ExpectedException thrown = ExpectedException.none();
}

and then inheriting from that:

class ExceptionsHappen extends ExpectedExceptionTest {

  @Test
  def badInt: Unit = {
    thrown.expect(classOf[NumberFormatException])
    Integer.parseInt("one")
  }
}

which works correctly.



回答2:

To make this work with JUnit 4.11 in Scala, you should meta-annotate your annotation so that the annotation is applied only to the (synthetic) getter method, not the underlying field:

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

class ExceptionsHappen {

  @(Rule @getter)
  def thrown = rules.ExpectedException.none

  @Test
  def badInt: Unit = {
    thrown.expect(classOf[NumberFormatException])
    Integer.parseInt("one")
  }
}


回答3:

As a very newbie to Scala I am just using a very simple workaround: explicitly catch the exception and fail if your expected exception is not thrown.

Below is a sample skeleton:

try {
  *your code that should throw an exception*
  fail("Did not generate *the.Exception.you.expect*")
} catch {
  case t: *the.Exception.you.expect* => // do nothing, it's expected :)
}


回答4:

Without knowing JUnit rules, and without testing it, because I don't have an appropriate setup at hand, I go out on a limb and suggest turning thrown into a val. I guess its some member that is initialized with something and then it gets some state and then some other machinery checks the state against something. You are always creating new ones and keep forgetting the expectation.



回答5:

If Scala has something similar like static imports, then catch-exception is an alternative to JUnit 4.7's ExpectedException @Rule.