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

RuleChain#around should not allow null args #1313

Merged

Conversation

alb-i986
Copy link
Contributor

@alb-i986 alb-i986 commented May 31, 2016

By throwing IllegalArgEx from around(),
we allow for better feedback to the final user,
as the stacktrace will point to the exact line where the null rule is declared.

Previously the stacktrace would have been something like this:

java.lang.NullPointerException
    at org.junit.rules.RunRules.applyAll(RunRules.java:26)
    at org.junit.rules.RunRules.<init>(RunRules.java:15)
    at org.junit.rules.RuleChain.apply(RuleChain.java:96)
    at org.junit.rules.RunRules.applyAll(RunRules.java:26)
    at org.junit.rules.RunRules.<init>(RunRules.java:15)
    at org.junit.runners.BlockJUnit4ClassRunner.withTestRules(BlockJUnit4ClassRunner.java:424)
    at org.junit.runners.BlockJUnit4ClassRunner.withRules(BlockJUnit4ClassRunner.java:378)
    at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:299)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:83)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:58)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)

As you can see, it was pretty hard to figure out that the null pointer was one of the rules in the chain.

Whereas with this change it is:

java.lang.IllegalArgumentException: The enclosed rule should not be null

    at org.junit.rules.RuleChain.around(RuleChain.java:84)
    at org.junit.rules.RuleChainTest$RuleChainWithNullRules.<init>(RuleChainTest.java:81)  <-----
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:228)
    at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:238)

where at org.junit.rules.RuleChainTest$RuleChainWithNullRules.<init>(RuleChainTest.java:81) points at the line where the null rule is appended.

Follows the sample test class used above:

public class RuleChainWithNullRules {
    @Rule
    public final RuleChain chain = outerRule(new LoggingRule("outer rule"))
            .around(null);

    @Test
    public void example() {}
}

By throwing IllegalArgEx from around(),
we allow for better feedback to the final user,
as the stacktrace will point to the exact line where the null rule is declared.

Previously the stacktrace would have been something like this:

java.lang.NullPointerException
	at org.junit.rules.RunRules.applyAll(RunRules.java:26)
	at org.junit.rules.RunRules.<init>(RunRules.java:15)
	at org.junit.rules.RuleChain.apply(RuleChain.java:96)
	at org.junit.rules.RunRules.applyAll(RunRules.java:26)
	at org.junit.rules.RunRules.<init>(RunRules.java:15)
	at org.junit.runners.BlockJUnit4ClassRunner.withTestRules(BlockJUnit4ClassRunner.java:424)
	at org.junit.runners.BlockJUnit4ClassRunner.withRules(BlockJUnit4ClassRunner.java:378)
	at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:299)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:83)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:58)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)

As you can see, it was pretty hard to figure out that the null pointer was one of the rules in the chain.
*/
public RuleChain around(TestRule enclosedRule) {
if (enclosedRule == null) {
throw new IllegalArgumentException("The enclosed rule should not be null");
Copy link
Member

Choose a reason for hiding this comment

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

We generally throw NullPointerException if someone passes in a null for a parameter that shouldn't be null (this is also what the Java Collections library does and what Effective Java recommends).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. Done, although I really prefer IllegalArgumentEx as I find it more meaningful than a NPE: NPEs can be thrown by the JVM, whereas IllegalArgEx cannot.
For example, take the unit test: since it's now considering a NPE as expected, I had to add an assertion on the message to make sure that the NPE is really what we are expecting, and not some other null reference (a programming error).

}

// TODO delete as soon as #1312 is merged
private static String stacktraceToString(Throwable e) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

In case you are going to merge #1312 soon, please merge that before this, so that I can remove this method.

@alb-i986
Copy link
Contributor Author

Ready

@@ -77,8 +77,12 @@ private RuleChain(List<TestRule> rules) {
*
* @param enclosedRule the rule to enclose.
Copy link
Member

Choose a reason for hiding this comment

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

Please change this line to @param enclosedRule the rule to enclose; must not be {@code null}.

@alb-i986
Copy link
Contributor Author

Any other concerns on this one?

@marcphilipp
Copy link
Member

LGTM, thanks!

@junit-team/junit-committers Please take a look!

@stefanbirkner
Copy link
Contributor

LGTM

@marcphilipp marcphilipp merged commit 7e38e39 into junit-team:master Jul 12, 2016
@marcphilipp
Copy link
Member

Thanks!

@alb-i986 Please add a description of this improvement to the draft of the release notes.

@alb-i986 alb-i986 deleted the RuleChain-around-reject-null-args branch July 12, 2016 21:11
@alb-i986
Copy link
Contributor Author

Please add a description of this improvement to the draft of the release notes.

Done:
https://github.com/junit-team/junit4/wiki/4.13-Release-Notes#pull-request-1313-rulechainaround-rejects-null-arguments

@marcphilipp
Copy link
Member

Thanks! 👍

jglick added a commit to jglick/active-directory-plugin that referenced this pull request Aug 21, 2020
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.

4 participants