TNG / junit-dataprovider

A TestNG like dataprovider runner for JUnit with many additional features
Apache License 2.0
246 stars 164 forks source link

Duplicate execution when test implements an interface #106

Closed x3rAx closed 6 years ago

x3rAx commented 6 years ago

Hi,

I've just started using junit-dataprovider and I'm also relatively new to Java / Java unit testing so please forgive me if I'm doing something completely wrong here.

Let me first provide some context:

I've created an interface that certain types of my tests should implement. This is because I have an abstract class and some child classes that should all share the functionality/interface of the abstract class. Therefore I have to write some tests multiple times for each child class. That's why an interface for my test classes comes in handy because I'm forced to implement all that test and can't forget some of them.

junit: v4.12 junit-dataprovider: v1.13.1 IDE:

Android Studio 3.0.1 Build #AI-171.4443003, built on November 9, 2017 JRE: 1.8.0_152-release-915-b01 amd64 JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o Linux 4.15.3-1-ARCH

$ uname -rsom # if that's of any relevance
Linux 4.15.3-1-ARCH x86_64 GNU/Linux

$ java -version
openjdk version "1.8.0_144"
OpenJDK Runtime Environment (build 1.8.0_144-b01)
OpenJDK 64-Bit Server VM (build 25.144-b01, mixed mode)

The issue:

I have noticed, that tests that are defined in the interface (as described above) and that are using the @UseDataProvider annotation, are run twice

Example:

public interface MyInterface<T> {
    void testSomething();
    void testSomethingWithData(T value);
}

public class MyTest implements MyInterface<String> {
    @DataProvider
    public static Object[][] myDataProvider() {
        return new Object[][] {
            "foo",
            "BAR",
        };
    }

    @Test
    @Override
    public void testSomething() {
        assertTrue(true);
    }

    @Test
    @UseDataProvider("myDataProvider")
    @Override
    public void testSomethingWithData(String value) {
        assertTrue(true);
    }

    @Test
    @UseDataProvider("myDataProvider")
    public void anotherTestWithData(String value) {
        assertTrue(true);
    }
}

The output would then be something like:

[OK] MyTest
  - [OK] testSomething
  - [OK] testSomethingWithData[0: foo]
  - [OK] testSomethingWithData[1: BAR]
  - [OK] testSomethingWithData[0: foo]
  - [OK] testSomethingWithData[1: BAR]
  - [OK] anotherTestWithData[0: foo]
  - [OK] anotherTestWithData[1: BAR]

As can be seen here, the testSomething which overrides the interface method, is run only once. Also the anotherTestWithData is only run once per provided value.

But the testSomethingWithData runs two times for each value.

aaschmid commented 6 years ago

Hi @x3rAx,

thanks for the nice and informative issue. I can reproduce your issue using IntelliJ. I have no Android Studio. The interesting thing is, though, that it works correctly in Eclipse, executing the same code snippet ...

This is quite strange but I will take some more time to debug it further.

How urgent is it on your side?

Cheers, Andreas

x3rAx commented 6 years ago

Hi @aaschmid,

it's just for a small project I'm contributing to in my free time. So it's not particularly urgent for me. As long as I can run them successfully I'm fine :)

I was just wondering what was going on there when I saw the duplicate test results and at first I thought it was my fault. But after some investigation I was certain that it was a bug and I thought, someone should mention it somewhere ;)

Cheers, Björn

aaschmid commented 6 years ago

Same issue occurs executing the test case in Gradle but only for JUnit4. Wrote same tests for junit-jupiter and junit-jupiter-params modules as well which work correctly ...

Debugging it further it seems to be a compiler issue caused by generic type parameter on the interface which strangely generates two methods instead of expected one:

Both methods are returned by getTestClass().getAnnotatedMethods(Test.class). As the Eclipse compiler doesn't generate two methods, this behavior does not occur using Eclipse.

aaschmid commented 6 years ago

The problem are known and the phenomenon is called "Bridge methods" caused due to type erasure, see also https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html.

This happens not in Eclipse, see https://stackoverflow.com/questions/44136834/interface-method%C2%B4s-annotations-are-inherited-in-java-7-but-not-in-java-8

aaschmid commented 6 years ago

Fixed it for new in junit-dataprovider junit4 module (even if JUnit4 should normally fix that itself).

Why does it work with JUnit5?