junit4 icon indicating copy to clipboard operation
junit4 copied to clipboard

@Parameters method is executed before @ClassRule initialization. Could it be the way around?

Open javornikolov opened this issue 11 years ago • 10 comments

I have the following problem (using junit 4.11):

    @ClassRule
    public static TemporaryFolder tmp = new TemporaryFolder();
    ...
    @Parameters
    public static Collection<Object[]> data() throws Exception {
        return java.util.Arrays.asList(new Object[][] {
            {0, tmp.getRoot().getPath()}
        });
    }

This results in initializationError

java.lang.IllegalStateException: the temporary folder has not yet been created
    at org.junit.rules.TemporaryFolder.getRoot(TemporaryFolder.java:127)

So seems the @Parameters method is executed before the ClassRule initialization phase which makes scenarios like above one a bit complicated.

javornikolov avatar May 02 '13 16:05 javornikolov

Current workaround:

    protected static TemporaryFolder initStaticTemp() {
        try {
            return new TemporaryFolder() { { before(); } };
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    public static TemporaryFolder tmp = initStaticTemp();

    @AfterClass
    public static cleanup() throws Exception {
        tmp.delete();
    }

It works but it needs that manual cleanup...

javornikolov avatar May 02 '13 18:05 javornikolov

+1

Thinking about it a bit I guess a good way to implement this would be to introduce an annotation for this:

@Parameters
@AfterClassRules
public Object[][] generateParameters() {
    // do stuff
}

This would ensure that both variants are still usable, and that existing code is not broken. If this design is acceptable to the maintainers I would start working on a pull request for this.

bjoernpollex avatar Oct 09 '13 09:10 bjoernpollex

I am writing an additional comment because I am not sure if an update on a comment triggers a notification.

Would the design proposed in my previous comment be acceptable to the maintainers? If so, I would start work on a pull-request.

bjoernpollex avatar Oct 11 '13 12:10 bjoernpollex

There's a pretty hard architectural problem here: JUnit promises runners like Eclipse that it can enumerate how many tests there are before any of the tests are run, but also wants to minimize any resources consumed during this planning phase. So we really do want to know how many parameters there are going to be before we, for example, create any temporary folders, or do the even more drastic stuff that shows up in ClassRules, like starting servers.

Perhaps my favorite answer would be to enable something like @DataPoints in Theories: there can be static fields or static methods prefixed with @ParameterSet, which are joined together to make the full parameter set. So your example would be:

@ClassRule public static TemporaryFolder tmp = new TemporaryFolder(); @ParameterSet public static Object[] first = new Object[] { 0 }; @ParameterSet public static Object[] second() { return new Object[] { tmp.getRoot().getPath(); } }

The point here is that we could count the method "second" without actually executing it.

What do you think?

dsaff avatar Oct 11 '13 20:10 dsaff

Hm, I guess now that I think about it, the problem is actually more a smell in my test code. I am connecting to a remote service and running tests depending on how it is configured. Instead I should probably just fix the expected configuration in my test.

bjoernpollex avatar Oct 11 '13 21:10 bjoernpollex

Here is the test where I originally faced the issue: CryptoAppExecReturnCodeTest.java

@dsaff I'll give some thought on your suggestion. Initial impression is: splitting pairs into separate arrays needs special care of elements' positions (a bit error prone). So I may end up filling up first with some dummy stuff (just maintaining the number of elements); and in second - I would like to keep the pair elements next to each other:

{ 0, tmp...getPath() },
{ 1, ... }

Is there anything else which runners need besides the number of tests? (E.g. - maybe test names?)

javornikolov avatar Oct 11 '13 23:10 javornikolov

@javornikolov, I just realized that I misread your initial post, and so my response was likely confusing. I was reading your test as two different instantiations of a test class that is constructed with one parameter; now that I read it again, it's actually one instantiation of a test class that is constructed with two parameters. To adjust my proposal then, the suggested code would be:

@ClassRule public static TemporaryFolder tmp = new TemporaryFolder();
@ParameterSet public static Object[] only() {
return new Object[] { 0, tmp.getRoot().getPath(); }
}

I hope that makes my intent clearer; sorry for my initial confusion.

dsaff avatar Oct 17 '13 04:10 dsaff

(going through some old bugs)

Perhaps we should rename this issue "Enable something like @DataPoints in Theories"?

kcooney avatar Jan 26 '14 07:01 kcooney

(going through some old bugs)

Perhaps we should rename this issue "Enable something like @DataPoints in Theories"?

I would say the issue is to have ability to use Rule resources in the list of parameters for parametrized tests. The approach with @ParameterSet proposed by @dsaff seems viable to me (assuming evaluations happen in such a sequence that original issue is resolved). But if "Something like @DataPoints" is clear enough and no risk to diverge in some direction not covering original scenario: OK for me. The reason I prefer Parametrized tests over Theories (which use DataPoints) is that the results of the latter are not reported independently for each set of params, and that first failure aborts the execution of the subsequent params.

javornikolov avatar Jan 31 '14 18:01 javornikolov

I tried something like this…

@RunWith(StepRunner.class) public class LaunchEnv { @ClassRule public static final CustomRunner tempFolder = new CustomRunner();

@Test
public void dummyTest() {
}

}

Call JUnitCore.runClasses(LaunchEnv.class); In the @Parameterized.Parameters method

balrajdacha avatar May 11 '21 14:05 balrajdacha