New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix test filtering for test parameterized using TestParameterInjector
.
#1556
Fix test filtering for test parameterized using TestParameterInjector
.
#1556
Conversation
Intellij junit plugin allows filtering tests by the method name. In case of parameterized tests, the name includes parameters as well (e.g. `test[parameter]`). By default, attempt to run a test with a parameter in the name would fail since a method like that cannot be found. We already special case `junit.Parameterized` to work around the problem. Extend logic handling parameterized runners to support [`TestParameterInjector`](https://github.com/google/TestParameterInjector). Do not fail on tests with arguments (allowed by `TestParameterInjector`). Please note that similar problem exists for other parameterization libraries. There are workarounds in some libraries (e.g. [Burst](square/burst@fac095a)), however one cannot properly filter on parameter values without change on the IDE side. For reference: [`TestParameterInjector` issue](google/TestParameterInjector#6).
cc7a3cb
to
010cba6
Compare
Could you please create e.g. service provider and the extension in the TestParameterInjector project, so IJ doesn't depend on custom junit runner? Thanks |
Thank you for the suggestion, but I think I may need to ask for some clarification. The dependency is there for tests only, not IJ itself. What this change is trying to achieve is to make the JUnit runner plugin recognize TestParameterInjector as one supporting parameterization. Do you have a specific idea in mind? If so, can you elaborate? |
Ok, I don't want IJ tests to depend on specific runner and I also don't want to have code in the listener which depends on specific runner. I suggest to have an extension in junit plugin (rt part) which would allow to recognise additional parameterised tests. Then simple plugin or even internal part of library (if it's applicable) could make the whole thing work. Yes, far more complicated than just put this code into junit plugin but this way we would allow people to customise runners and not to put everything in the platform |
I am happy to delete the tests. That would remove the library dependency. I presume by the listener depending on the runner you mean the mention of it's name there?
How would you envision that being recognized by the plugin? Do you want to introduce a new marker interface/annotation to be applied on the JUnit runners? If that's the idea, where should that new interface/annotation live? I would think it is not ideal for the test runner libraries to depend on the junit plugin of Intellij. Should we be adding that to something like https://github.com/JetBrains/java-annotations? If you have a different idea of how to recognize the custom runners, can you please elaborate on your idea (including how they would depend on it)? |
I suggest to introduce service provider and load extensions in runtime if user has additional dependency on the classpath. This means that that jar may be loaded by the user explicitly, be a part of IJ plugin or be a part of the library. When you define derive providers you need api which you would like to load. And then the provider must recognise runner classes or directly create a request for IJ runner. |
Question @ Anna: Would you accept this PR with only the change to |
I'd like to avoid any dependencies on custom runners. If you can ensure me that suggested plan won't work, I can reconsider but junit 5 works this way and I would rather ask you to move in that direction instead |
Are you talking about this change?
--> You consider a hard-coded string that is the canonical name of TestParameterInjector a dependency? |
Yes, I consider the change which refers to some custom runner as third-party dependency. Would you include dependency on IJ internals as strings as part of your library? It would be easy to write an extension on IJ side and users won't need additional plugin or another dependency. |
I think I'm starting to understand what you're saying. So instead of this: private static boolean isParameterizedRunner(Class<? extends Runner> runnerClass) {
return runnerClass.getCanonicalName().equals("com.google.testing.junit.testparameterinjector.TestParameterInjector")
} you want something like this: // ******** Defined in new file in IntelliJ codebase ******** //
package com.intellij.junit4;
interface ParameterizedTestMetadataProvider {
boolean isParameterizedRunner(Class<? extends Runner> runnerClass);
}
// ******** In JUnit4TestRunnerUtil.java ******** //
private static boolean isParameterizedRunner(Class<? extends Runner> runnerClass) {
for(clazz in classpath) {
if(clazz extends ParameterizedTestMetadataProvider) {
if(clazz.getConstructor().newInstance().isParameterizedRunner(runnerClass)) {
return true;
}
}
}
}
// ******** In TestParameterInjector codebase ******** //
class TestParameterInjectorIntellijMetadataProvider implements ParameterizedTestMetadataProvider {
// ... implement isParameterizedRunner ...
} ? |
This is kind of one of the proposals I had in my comment above -- that's a more complicated marker interface. Is that something you would accept? Another one would be annotate the runner with something: // ******** Somewhere within the Intellij codebase -- Anna, please share where it would be ******** //
public @interface ParameterizedTestRunner {
}
// ******** In JUnit4TestRunnerUtil.java ******** //
private static boolean isParameterizedRunner(Class<? extends Runner> runnerClass) {
return Parameterized.class.isAssignableFrom(runnerClass) || ArrayUtils.contains(runnerClass.getDeclaredAnnotations(), ParameterizedTestRunner.class);
}
// ******** In TestParameterInjector codebase ******** //
@ParameterizedTestRunner
class TestParameterInjectorIntellijMetadataProvider {
// ... implement isParameterizedRunner ...
} If any of these are acceptable, can you please share a location where you would accept adding such an an interface/annotation? The reason we are asking for details is because I am more than happy to do the work which you would deem up to quality, however I would like us to agree on how that would look like before I do that to avoid any misunderstandings down the line. If there is more details you would like from the proposals, please let me know -- I will be more than happy to elaborate. |
Yes, exactly! I am totally fine with the "current" location:
|
Great! Can you help me understand what Maven artifact that would live in (test runners will need to be able to import that)? |
Friendly ping @ Anna on this question? Also, I'd like to propose an alternative that avoids dependencies between our systems in both directions:
This has all the advantages that the provider interface has, but saves IntellIJ from exposing an interface and saves the TestParameterInjector of taking a binary dependency on an IntelliJ artifact. What do you think? |
I don't know what do you want to get from me: I suggest to create IJ plugin, then you don't need a maven artefact for that but you just use ij gradle plugin. I am also ok with the common annotation though you would need to put it in junit artefact or publish an artefact and pursue junit to use it. Otherwise it would be exactly the same as now: dependency on one custom runner which I would like to avoid. |
I don't think this is true. Any parameterized test framework can define their own There is nothing about this solution that is specific to either IntelliJ or The other solutions you're proposing don't sound particularly appealing:
|
Anybody can use some maven artefact = nobody would ever find it and would never use it. Moreover as far as I know, JUnit 4 is not developed anymore. Why not junit 5? |
Because TestParameterInjector is a JUnit4 runner. Almost all Google tests use JUnit4 so this isn't going to change anytime soon.
I don't understand this argument because:
To recapYour original point was:
My proposal that uses |
You assume that all that 10 people would go and fix their libraries. I have an experience that it doesn't happen at all or at least not fast. Ok, suppose it's done. Let's talk about the name: it should reflect that it's about junit 4 runner and parameterised tests and then about brackets. Normally, annotation names are not verbs. Naming is hard... |
They may not need to fix anything at all, right. The status quo might be working for them.
How about |
Not to detract from choosing the best annotation name, but I wanted to point out that in a prior comment suggested adding an annotation to a Jetbrains-owned library which contains annotations to communicate facts about the code to the IDE: https://github.com/JetBrains/java-annotations -- do you think you could state your opinion about that? The upside of that is if the annotation was imported, we could properly document what it does. If java-annotations is not a right place, please let me know if another Jetbrains-owned library would work (Jetbrains-owned since we are adding it only because of Intellij)? If none, then hard-coded names seem like an only option. |
I have 3 similar issues, I'll try to come up with the common solution and will write here the results |
The fix will be available in the first 2021.3 EAP. My commit should be available on github later today/tomorrow. Thanks |
Thank you for doing that. I looked at your change but it looks like it leaves out an important aspect of the parameterized runners, namely:
Same follows for Burst. |
You are welcome to create a PR with check that class has constructor with parameters. I don't know how to detect boolean fields which are supposed to be injected by the test framework though. |
Intellij junit plugin allows filtering tests by the method name. In case of parameterized tests, the name includes parameters as well (e.g.
test[parameter]
). By default, attempt to run a test with a parameter in the name would fail since a method like that cannot be found. We already special casejunit.Parameterized
to work around the problem.Extend logic handling parameterized runners to support
TestParameterInjector
. Do not fail on tests with arguments (allowed byTestParameterInjector
).Please note that similar problem exists for other parameterization libraries. There are workarounds in some libraries (e.g. Burst), however one cannot properly filter on parameter values without change on the IDE side.
For reference:
TestParameterInjector
issue.