Skip to content
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

[#325] Spock 2 test support #326

Merged
merged 5 commits into from
Oct 26, 2022
Merged

Conversation

szpak
Copy link
Contributor

@szpak szpak commented Aug 21, 2022

And most likely also other 3rd-party JUnit Platform engines.

Also bumped surefire version and added some toString() methods for better reporting in tests.

TODO: All tests pass, but while testing in Idea 2022.1, the Spock test classes are detected as changed, but not executed. I have to bring back how it was possible to debug "live" Infinitest in Idea :). Maybe you will have an idea why?

Fixed #325.

@szpak
Copy link
Contributor Author

szpak commented Aug 21, 2022

isATest = !isAbstract(classReference) &&
hasTests(classReference) &&
(isJUnit5TestClass(imports) || canInstantiate(classReference));
className = classReference.getName();
}
private boolean isJUnit5TestClass(final String[] imports) {
return stream(imports)
.anyMatch(org.junit.jupiter.api.Test.class.getName()::equals);
}
seems to be a culprit. I wonder how those 2 (3) places (here and DefaultRunner) are sharing responsibility of tests detection?

(a quick a dirty check for Spock tests fixes the execution, but it would be definitely "for Spock only", so we loose the ability to find any junit-platform engine's test - so maybe you have a better idea)

import java.util.List;

import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;

Copy link
Contributor Author

@szpak szpak Aug 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if Junit5Runner shouldn't be renamed to JUnit5PlatformRunner (or something similar) after these changes?

@szpak
Copy link
Contributor Author

szpak commented Aug 21, 2022

After rebased with master, I have the same mockito error, however, I'm not sure yet why it works in master, as I seem to change code in the other places...

@gtoison
Copy link
Contributor

gtoison commented Aug 21, 2022

Hey @szpak thanks for the contribution and welcome back!
Please bear with me as I'm still trying to wrap my head around Infinitest... actually I would also love to know how to debug Infinitest in Idea (or Eclipse)!
I've just upgraded surefire in #329 and now it seems to report unused mockito stubbings, so I fixed those to. It should be better after rebasing.

@gtoison
Copy link
Contributor

gtoison commented Aug 21, 2022

isATest = !isAbstract(classReference) &&
hasTests(classReference) &&
(isJUnit5TestClass(imports) || canInstantiate(classReference));
className = classReference.getName();
}
private boolean isJUnit5TestClass(final String[] imports) {
return stream(imports)
.anyMatch(org.junit.jupiter.api.Test.class.getName()::equals);
}

seems to be a culprit. I wonder how those 2 (3) places (here and DefaultRunner) are sharing responsibility of tests detection?

(a quick a dirty check for Spock tests fixes the execution, but it would be definitely "for Spock only", so we loose the ability to find any junit-platform engine's test - so maybe you have a better idea)

I think that JavaAssistClass unfortunately needs to replicate the "is it a test" logic of various framework because it discovers tests without actually loading the corresponding classes.
That seems to be a reasonable decision because loading the class within the plugin would create all kind of problems, for instance when the class is recompiled or because it would also need to load all the dependencies.
On the other hand the runner does load the classes (since it will run them) so it can use the JUnit built-in code to check if a class is a test

Looking at the metadata with javaassist "for Spock only" sounds good to me.

@szpak
Copy link
Contributor Author

szpak commented Aug 21, 2022

Keeping infinitest-lib not aware of testing frameworks have sense. Looking at its (our method) usage, it seems to be created to detect test among changed files and pass them to the runner. However, having some future changes around, you could check how it impacts performance (or other thing) to filter it out at the lib level (as it limits the vision to support of junit-platform engines at once :-( ).

Regarding loading the framework related class, it seems the junit-jupiter "was leaked" as a infinitest-lib dependency:

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit5.version}</version>
</dependency>

and it's effectively loaded:

private boolean isJUnit5TestClass(final String[] imports) {
return stream(imports)
.anyMatch(org.junit.jupiter.api.Test.class.getName()::equals);

Doing the Spock stuff, I can also improve that place to load detect it by name (probably a separate PR to be able to rollback the Spock stuff, just in case ;-) ).

@gtoison
Copy link
Contributor

gtoison commented Aug 22, 2022

My comments about loading the classes were referring to the user's tests, I think it is fine if JUnit is loaded in the plugin.
At the same time this made me realise that the lib module also gets JUnit through the dependency to the runner and that dependency only seems to be for one exception.
I think it's better to leave these dependencies alone for now, so we don't break too many things at the same time.
The lack of integration tests actually running Infinitest on various sample projects scares me a little.

I'm not sure if you noticed it but I think that the important logic for JUnit tests detection is the one checking if a method is a test method, I think that the check on imports is a last resort.

So I think it's fine to put something specific to Spock here. In case this becomes a problem it shouldn't be too difficult to make it more modular, for instance by checking what test libraries are on the classpath.


void setup() {
classPool = new ClassPool()
classPool.appendSystemPath()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's enough in Idea, but with Maven, the tests fails with:

[ERROR]   JavaAssistClassSpockSpec.should detect Spock 2 test (#clazz.simpleName):21 Condition failed with Exception:

classFor(clazz).isATest()
|        |
|        class org.infinitest.testdata.SimpleSpockSpec
java.lang.RuntimeException: javassist.NotFoundException: org.infinitest.testdata.SimpleSpockSpec
	at org.infinitest.parser.JavaAssistClassSpockSpec.classFor(JavaAssistClassSpockSpec.groovy:30)
	at org.infinitest.parser.JavaAssistClassSpockSpec.should detect Spock 2 test (#clazz.simpleName)(JavaAssistClassSpockSpec.groovy:21)
Caused by: javassist.NotFoundException: org.infinitest.testdata.SimpleSpockSpec
	at javassist.ClassPool.get(ClassPool.java:450)
	at org.infinitest.parser.JavaAssistClassSpockSpec.classFor(JavaAssistClassSpockSpec.groovy:28)
	... 1 more

I extracted it to a separate module to do not have to put Spock 2 on the class path (and possibly any other testing framework which would start to be supported). Maybe you have an idea for the workaround?

@@ -409,4 +423,37 @@ public boolean locatedInClassFile() {
public File getClassFile() {
return classFile;
}

private static class SpockIsTestClassChecker {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is quite big, so I created an inner class to (at least directly) do not enlarge it. Assuming, there is no way to avoid checking for testing frameworks in the lib module, probably it would be good to extract support for the particular frameworks to separate classes.

And most likely also other 3rd-party JUnit Platform engines.

Also bumped surefire version and added some toString() methods for better reporting in tests.
Checking in the lib module.
@gtoison gtoison added the comp:core core feature of infinitest (infinitest-lib + infinitest-runner)) label Aug 24, 2022
@gtoison
Copy link
Contributor

gtoison commented Oct 23, 2022

Hello @szpak, I've made some fixes to get the tests working, and also modified the "is it a test" logic to look for the @Testable annotation instead of Spock's Specification
I've also merged in the recent changes from the master branch

Can you please give me write access to this branch on your repo, so I can push my changes there?

@szpak
Copy link
Contributor Author

szpak commented Oct 23, 2022

Cool, I will take a look!

I gave you a RW access to this branch when I originally made a PR (unless GitHub changed something in that matter recently). Have you tried and failed?

Update. I don't see "Allow edits from maintainers." checkbox in this PR (as mentioned in docs), maybe it is a reason?

@szpak szpak marked this pull request as ready for review October 23, 2022 15:49
@szpak szpak marked this pull request as draft October 23, 2022 15:49
@szpak
Copy link
Contributor Author

szpak commented Oct 23, 2022

Hello @szpak, I've made some fixes to get the tests working, and also modified the "is it a test" logic to look for the @testable annotation instead of Spock's Specification

Hmm, interesting. Spock - as far as I know - doesn't use it itself. However, if it works for you, I will take a look (once merged into this branch) to explain why :-).

- Testable is more generic, this might pick up tests from other
frameworks as well
- Use infinitest-lib utility class to get the tests ClassPool
@gtoison
Copy link
Contributor

gtoison commented Oct 23, 2022

I'm not sure why I can't push my change (I don't seem to have the permissions) so I've submitted here: szpak-forks#1
I've reverted most of the changes in JavaAssistClass so compared to the master the change is only looking for @Testable in the class or its parents

@szpak
Copy link
Contributor Author

szpak commented Oct 23, 2022

Cool, I merged your change manually with fast-forward and after a quick look, @Testable is from the platform, it might be possible to detect tests recognized by the JUnit Platform.

Regarding write permissions, I created that form many years ago. Probably that feature was not available right there. I added you to the repository directly, if you want to add anything else.

className = classReference.getName();
}

private boolean isJUnit5TestClass(final String[] imports) {
private boolean hasJUnit5TestImport(final String[] imports) {
return stream(imports)
.anyMatch(org.junit.jupiter.api.Test.class.getName()::equals);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's outside a scope of this PR, but probably @Testable could be used also for JUnit Jupiter tests.

@szpak
Copy link
Contributor Author

szpak commented Oct 23, 2022

I will play with it more tomorrow, but in general it looks good.

@gtoison
Copy link
Contributor

gtoison commented Oct 24, 2022

Thank you! You seem to be the author of the ClassPoolForFakeClassesTestUtil that fixed the issues with the ClassPool, but that was a while ago :)
From what I've seen the problem was that JavaAssist could not load the Specification class at all, so it could check if that was the parent class (or check if it had the @Testable annotation).
This is a bit specific to Infinitest's own tests, when actually running it should get the classpath of the project under test and the Spock classes should be available

@szpak
Copy link
Contributor Author

szpak commented Oct 24, 2022

Thank you! You seem to be the author of the ClassPoolForFakeClassesTestUtil that fixed the issues with the ClassPool, but that was a while ago :)

Adding support for TestNG in 2013... I could forgot about that :-).

Regarding @Testable, it is definitely used in spock.lang.Specification. Previously, I did just a quick check in GitHub and its seach field hadn't returned any result (possibly I made a typo or something...).

@szpak szpak marked this pull request as ready for review October 24, 2022 21:12
@gtoison
Copy link
Contributor

gtoison commented Oct 26, 2022

Thanks a lot for the contribution!

@gtoison gtoison merged commit 1dbc697 into infinitest:master Oct 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp:core core feature of infinitest (infinitest-lib + infinitest-runner))
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for Spock 2 tests (junit-platform based)
2 participants