0

I want to log the unit test names in Scala automatically. The solution with org.junit.rules.TestName works fine in Java, but not in Scala.

Consider the following code snippet:

import org.junit._
import org.junit.rules.TestName

class ScalaUnitTestExample {
    @Rule val testName = new TestName

    @Before def printTestCaseNameBefore() {
        print("\nStart of test case " + testName.getMethodName)
    }

    @After def printTestCaseNameAfter() {
        print("\nEnd of test case " + testName.getMethodName)
    }

    @Test
    def checkAddition() {
        Assert.assertEquals(5, 2 + 3)
    }

    @Test
    def checkMultiplication() {
        Assert.assertEquals(6, 2 * 3)
    }
}

It compiles fine, but when running I receive the following error message:

There was 1 failure:
1) initializationError(ScalaUnitTestExample)
java.lang.Exception: The @Rule 'testName' must be public.
        at org.junit.internal.runners.rules.RuleFieldValidator.addError(RuleFieldValidator.java:90)
        (...)
        at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

FAILURES!!!
Tests run: 1,  Failures: 1

The Java counterpart works as expected.

I've already tried the following (according to Using JUnit @Rule with ScalaTest (e.g. TemporaryFolder)):

val _testName = new TestName
@Rule def testName = _testName

but this did not really help. In this version the test cases run, but the {testName} results always {null}.

The default access rule in Scala is public, and therefore there is no public keyword. On the other hand, the JUnit framework seems to consider it non-public.

Does anyone know how to overcome this problem? Another similar solution would also be fine! Thank you!

Community
  • 1
  • 1
Csaba Faragó
  • 443
  • 1
  • 4
  • 14
  • Why don't you use the ScalaTest instead? You'll be able to see the Test name in the logs and there are alternatives for the `@Before` and `@After`. – Zyoma Mar 22 '16 at 10:59
  • The issue with the first option is that `val testName` defines both a field and a getter method (in Java terms); the annotation goes to the field by default (but this can be changed: see http://www.scala-lang.org/api/2.11.8/#scala.annotation.meta.package); the field is private (Scala doesn't allow defining non-private fields at all), but JUnit requires it to be public. It's surprising to me that the second option doesn't work. – Alexey Romanov Mar 22 '16 at 11:23

3 Answers3

1

As you know scala default access rule is public, but scala does a trick, the field is private and it generates getters and setters, and that is the reason @Rule does not work in your code.

You can make it work using

import org.junit._
import org.junit.rules.TestName


    class ScalaUnitTestExample {
      var _testName: TestName = new TestName

      @Rule
      def testName = _testName
      def testName_=(aTestName: TestName) {_testName = aTestName}

      @Before def printTestCaseNameBefore() {
        print("\nStart of test case " + testName.getMethodName)
      }

      @After def printTestCaseNameAfter() {
        print("\nEnd of test case " + testName.getMethodName)
      }

      @Test
      def checkAddition() {
        Assert.assertEquals(5, 2 + 3)
      }

      @Test
      def checkMultiplication() {
        Assert.assertEquals(6, 2 * 3)
      }
    }

enter image description here

Anyway using ScalaTest or Specs2 would make your life easier.

Jonas Anso
  • 2,057
  • 14
  • 13
  • Thanks for the hint! Unfortunately your solution results always `null` value. I'll check the other proposals. – Csaba Faragó Mar 22 '16 at 12:27
  • I do not know what might be the problem. I did run it myself and worked for me. – Jonas Anso Mar 22 '16 at 13:07
  • Thanks for adding the screenshot! As I see you are using Linux; I have Windows 7. What artifacts versions are you using? My Scala version is 2.11.7, and the JUnit version is 4.9. – Csaba Faragó Mar 22 '16 at 13:47
  • Clone this repo and run "sbt test" it should work in windows. https://github.com/jonasanso/junit-test-scala – Jonas Anso Mar 22 '16 at 14:25
  • I did but the result is the same: Start of test case null End of test case null Start of test case null End of test case null[info] Passed: Total 2, Failed 0, Errors 0, Passed 2 [success] Total time: 22 s, completed 2016.03.22. 18:01:52 – Csaba Faragó Mar 22 '16 at 17:05
  • Thanks for testing, I am sorry I can not help more. Good luck and try out scala test. – Jonas Anso Mar 22 '16 at 22:03
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/107099/discussion-between-csaba-farago-and-jonasanso). – Csaba Faragó Mar 23 '16 at 07:22
0

Solution (with a light hack): put the method name logic into a base class written in Java. Java source (UnitTestBase.java):

import org.junit.*;
import org.junit.rules.TestName;

public class UnitTestBase {
    @Rule public TestName testName = new TestName();

    @Before public void printTestCaseNameBefore() {
        System.out.println("Start of test case " + testName.getMethodName());
    }

    @After public void printTestCaseNameAfter() {
        System.out.println("End of test case " + testName.getMethodName());
    }
}

Scala source (ScalaUnitTestExample.scala):

import org.junit._

class ScalaUnitTestExample extends UnitTestBase {
    @Test
    def checkAddition() {
        Assert.assertEquals(5, 2 + 3)
    }

    @Test
    def checkMultiplication() {
        Assert.assertEquals(6, 2 * 3)
    }
}

Compile:

scalac -cp .;junit-4.9.jar UnitTestBase.java ScalaUnitTestExample.scala

Run:

scala -cp .;junit-4.9.jar org.junit.runner.JUnitCore ScalaUnitTestExample

Result:

JUnit version 4.9
.Start of test case checkMultiplication
End of test case checkMultiplication
.Start of test case checkAddition
End of test case checkAddition

Time: 0,005

OK (2 tests)
Csaba Faragó
  • 443
  • 1
  • 4
  • 14
0

Use JUnit 4.12, I used @Jonas Anso's solution and it worked with that version, it did not work in 4.8 and 4.10. We have to set @Rule on a method, not on a field, seems that is ignored in 4.8 and 4.10. Also only getter is needed.

user10439725
  • 129
  • 7