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

Issue433 adapters for junit4 rules #526

Closed
wants to merge 31 commits into from

Conversation

mmerdes
Copy link
Contributor

@mmerdes mmerdes commented Sep 28, 2016

Overview

This PR provides an initial implementation for #433.

Both, field and method rules are supported.
Implementations of org.junit.rules.Verifier and org.junit.rules.ExternalResource are supported. Currently, this supported is activated by an explicit @ExtendWith.
This might be replaced by a classpath-based activation (expensive) or better by some sort of meta-annotation, e.g. @EnableJUnit4RuleSupport.
Currently, there is no warning issued if an unsupported rule type is used.


I hereby agree to the terms of the JUnit Contributor License Agreement.

@bechte
Copy link
Contributor

bechte commented Oct 1, 2016

First of all, thanks @mmerdes for the effort. Do I understand your code right, that it will require a compile time dependency to the JUnit4 core to support the JUnit4 @Rule annotation and the proposed rules as of #433 (+comments)? If we go along the route, this should be at least only a runtime dependency. .o(This would only be a minor thing to tackle)

Reading the tests for your implementation is giving me a good look & feel, how this thing will be used in the future. It comes along with advantages for test migration, i.e. the basic rules would require no change to the code. Still I feel, that we open the door for expectation far to wide. Allowing the basic rules will grow expectations for the other rules to follow. We might end up loosing the control of the test execution the same way as JUnit4 with @Rules.

As @kcooney already mentioned on #169 (comment), most rules only require to participate one or many of the three phases "setup", "teardown", and "afterEach". Our extension model allows exactly this (and that's the way you've implemented your adapters). The only thing that is missing is, to allow people to register an extension at the field level. If we had @ExtendWith for fields, one could simple replace the @Rule annotation from JUnit4 with @ExtendWith annotation from JUnit5 during the migration. The tests have to been transformed anyway to reference the JUnit5 version of the @Test annotation, therefore, it wouldn't be much hassle to also replace the @Rule.

Now, as we already discussed, @ExtendWith on the field level doesn't read well and it somehow behaves different (it is not registering an extension for its own level and all tests below the test hierarchy, but it adds another extension to the class level). Therefore, I would vote for using a different annotation that communicates its intend better, and provides the functionality needed, i.e. registering extensions on the field level: @UseExtension or @WithExtension

The annotation will have no values as the Type of the field should resolve to an Extension class. With this mechanism in place a migration of the rules included in #433 (+comments) would be possible like this:

// code before migration
@Rule
public TemporaryFolder folder = new TemporaryFolder();

@Rule
public ExternalResource resource1 = new ExternalResource() {
   // ...
}

// code after migration
@UseExtension
public TemporaryFolder folder = new TemporaryFolder();

@UseExtension
public ExternalResource resource1 = new ExternalResource() {
   // ...
}

Note these 3 things on the code:

  • @Rule will be replaced by @UseExtension (or @WithExtension, you-name-it)
  • TemporaryFolder will not be resolved to the JUnit4 Rule org.junit.rules.TemporaryFolder but to a new class that we have to provide which basically does the same thing.
  • ExternalResource will not be resolved to the JUnit4 Rule org.junit.rules.ExternalResource but to a new class that we have to provide which basically does the same thing.

Still, very simple and the migration could be done by a script, searching & replacing the parts. This way, I would have a much better feeling. If the wants me to provide this as an alternative approach, I would be happy to do so. But please let us take very careful steps with this topic. I have a really bad feeling about opening the gates to modify the test execution lifecycle.

@mmerdes
Copy link
Contributor Author

mmerdes commented Oct 6, 2016

@bechte
Thanks for your extended review.
Yes, a runtime dependency to JUnit 4 is of course required. As stated in the description of this PR the exact mechanics are not yet fixed. I outlined tow options and stated my preference. So far so good.
Concerning the main part of your comment:
If I understand you correctly, you are not comfortable with the requirements of this issue. However, these requirements have been fixed long ago in the respective issue and specifically included the verbatim use of the JUnit 4 @Rule syntax without the need for a script transformation etc.
As an additional approach field-based extensions are discussed here in #497.
I think we all agree that multiple migration options might be suitable for different companies/situations.

@mmerdes mmerdes force-pushed the issue433-adapters-for-junit4-rules branch 4 times, most recently from 74ac611 to ec4b3de Compare November 4, 2016 12:30
@mmerdes mmerdes force-pushed the issue433-adapters-for-junit4-rules branch from 59620e2 to 12e4e07 Compare November 18, 2016 16:18
Matthias Merdes and others added 23 commits November 18, 2016 17:23
limited support for JUnit 4 rules of type org.junit.rules.ExternalResource
optional dependency of the Jupiter engine on JUnit 4

#433
(cherry picked from commit a20fd0d)
add tests for the initial rule support.

#433
new integration test with 2 rules
and after invocation verification

#433
Refactor ExternalResourceSupport to prepare for rule-annotated methods
and other TestRules

#433
Support rule-annotated methods and
make the afterAll-check fail the build if appropriate.

#433
Refactor ExternalResourceSupport to handle the
method and field cases in separate (sub)classes.
The ExternalResourceSupport is now composed of the two separately implemented
support classes for method and field support, respectively.
Includes a test to verify the relative order of both cases.

#433
Includes a test case that verifies inherited rule-annotated
fields and methods are handled correctly
Refactoring to prepare the generalization from
ExternalResource to other suitable subtypes of TestRule
…from JUnit 4

This test would fail correctly if commented in so serves as documentation only.

#433
@mmerdes mmerdes force-pushed the issue433-adapters-for-junit4-rules branch from 12e4e07 to 7b25a68 Compare November 18, 2016 16:47
Copy link
Member

@marcphilipp marcphilipp left a comment

Choose a reason for hiding this comment

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

Overall I think this looks like it's almost good to go. I just found a few minor things. 👍


private void failIfAdapteeClassIsNotAssignableFromTargetClass() {
if (!this.adapteeClass.isAssignableFrom(this.target.getClass()))
throw new IllegalStateException(this.adapteeClass + " is not assignable from " + this.target.getClass());
Copy link
Member

Choose a reason for hiding this comment

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

How about using Preconditions?

ReflectionUtils.invokeMethod(method, target);
}
catch (NoSuchMethodException | SecurityException exception) {
LOG.warning(exception.getMessage());
Copy link
Member

Choose a reason for hiding this comment

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

I think this should throw an exception instead of logging it. Otherwise, it would only be visible in a log that something was silently ignored.

@Override
public void beforeEach(TestExtensionContext context) throws Exception {
this.fieldSupport.beforeEach(context);
this.methodSupport.beforeEach(context);
Copy link
Member

Choose a reason for hiding this comment

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

This should be methods first, then fields to maximize compatibility with Vintage.

Collectors.toList());
}

// TODO: decide whether this should be promoted to AnnotationUtils
Copy link
Member

Choose a reason for hiding this comment

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

I think it should but not necessarily in this pull request. Can you please create an issue for it and delete this comment?

this.testRuleInstance = (TestRule) testRuleField.get(testInstance);
}
catch (IllegalAccessException exception) {
LOG.warning(exception.getMessage());
Copy link
Member

Choose a reason for hiding this comment

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

Same here.

public class RuleAnnotatedMethod extends AbstractRuleAnnotatedMember {

public RuleAnnotatedMethod(Object testInstance, Method member) {
//no args
Copy link
Member

Choose a reason for hiding this comment

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

?

return ReflectionUtils.invokeMethod(method, target, arguments);
}
catch (NoSuchMethodException | SecurityException exception) {
LOG.warning(exception.getMessage());
Copy link
Member

Choose a reason for hiding this comment

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

I think this should throw an exception instead of logging it. Otherwise, it would only be visible in a log that something was silently ignored.

@marcphilipp marcphilipp added this to the 5.0 M3 milestone Nov 19, 2016
@marcphilipp marcphilipp self-assigned this Nov 22, 2016
@marcphilipp
Copy link
Member

in progress

@marcphilipp
Copy link
Member

This pull request has been manually rebased onto master and merged.

@mmerdes Thanks for all your hard work! 👍

@marcphilipp marcphilipp deleted the issue433-adapters-for-junit4-rules branch November 27, 2016 09:56
@sbrannen
Copy link
Member

+1: thanks, @mmerdes! 👍

I'm looking forward to hearing what the community has to say about this migration support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants