Skip to content

Commit 99b0ebc

Browse files
committed
Resolved Issue#589 where the method rules returned my methods were not executed
1 parent 26f9eba commit 99b0ebc

File tree

3 files changed

+150
-19
lines changed

3 files changed

+150
-19
lines changed

Diff for: src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java

+9-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.junit.internal.runners.statements.InvokeMethod;
2121
import org.junit.internal.runners.statements.RunAfters;
2222
import org.junit.internal.runners.statements.RunBefores;
23+
import org.junit.rules.MethodRule;
2324
import org.junit.rules.RunRules;
2425
import org.junit.rules.TestRule;
2526
import org.junit.runner.Description;
@@ -376,9 +377,14 @@ private List<org.junit.rules.MethodRule> getMethodRules(Object target) {
376377
* @return a list of MethodRules that should be applied when executing this
377378
* test
378379
*/
379-
protected List<org.junit.rules.MethodRule> rules(Object target) {
380-
return getTestClass().getAnnotatedFieldValues(target, Rule.class,
381-
org.junit.rules.MethodRule.class);
380+
protected List<MethodRule> rules(Object target) {
381+
List<MethodRule> rules = getTestClass().getAnnotatedMethodValues(target,
382+
Rule.class, MethodRule.class);
383+
384+
rules.addAll(getTestClass().getAnnotatedFieldValues(target,
385+
Rule.class, MethodRule.class));
386+
387+
return rules;
382388
}
383389

384390
/**

Diff for: src/main/java/org/junit/runners/model/TestClass.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,16 @@ public <T> List<T> getAnnotatedMethodValues(Object test,
244244
List<T> results = new ArrayList<T>();
245245
for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) {
246246
try {
247-
Object fieldValue = each.invokeExplosively(test);
248-
if (valueClass.isInstance(fieldValue)) {
247+
/*
248+
* A method annotated with @Rule may return a @TestRule or a @MethodRule,
249+
* we cannot call the method to check whether the return type matches our
250+
* expectation i.e. subclass of valueClass. If we do that then the method
251+
* will be invoked twice and we do not want to do that. So we first check
252+
* whether return type matches our expectation and only then call the method
253+
* to fetch the MethodRule
254+
*/
255+
if (valueClass.isAssignableFrom(each.getReturnType())) {
256+
Object fieldValue = each.invokeExplosively(test);
249257
results.add(valueClass.cast(fieldValue));
250258
}
251259
} catch (Throwable e) {

Diff for: src/test/java/org/junit/tests/experimental/rules/MethodRulesTest.java

+131-14
Original file line numberDiff line numberDiff line change
@@ -70,21 +70,20 @@ public void ruleIsIntroducedAndEvaluatedOnSubclass() {
7070

7171
private static int runCount;
7272

73-
public static class MultipleRuleTest {
74-
private static class Increment implements MethodRule {
75-
public Statement apply(final Statement base,
76-
FrameworkMethod method, Object target) {
77-
return new Statement() {
78-
@Override
79-
public void evaluate() throws Throwable {
80-
runCount++;
81-
base.evaluate();
82-
}
83-
84-
;
85-
};
86-
}
73+
private static class Increment implements MethodRule {
74+
public Statement apply(final Statement base,
75+
FrameworkMethod method, Object target) {
76+
return new Statement() {
77+
@Override
78+
public void evaluate() throws Throwable {
79+
runCount++;
80+
base.evaluate();
81+
}
82+
};
8783
}
84+
}
85+
86+
public static class MultipleRuleTest {
8887

8988
@Rule
9089
public MethodRule incrementor1 = new Increment();
@@ -292,4 +291,122 @@ public void foo() {
292291
public void useCustomMethodRule() {
293292
assertThat(testResult(UsesCustomMethodRule.class), isSuccessful());
294293
}
294+
295+
public static class HasMethodReturningMethodRule {
296+
private MethodRule methodRule = new MethodRule() {
297+
298+
@Override
299+
public Statement apply(final Statement base, FrameworkMethod method, Object target) {
300+
return new Statement() {
301+
302+
@Override
303+
public void evaluate() throws Throwable {
304+
wasRun = true;
305+
base.evaluate();
306+
}
307+
};
308+
}
309+
};
310+
311+
@Rule
312+
public MethodRule methodRule() {
313+
return methodRule;
314+
}
315+
316+
@Test
317+
public void doNothing() {
318+
319+
}
320+
}
321+
322+
/**
323+
* If there are any public methods annotated with @Rule returning a {@link MethodRule}
324+
* then it should also be run.
325+
*
326+
* <p>This case has been added with
327+
* <a href="https://github.com/junit-team/junit/issues/589">Issue #589</a> -
328+
* Support @Rule for methods works only for TestRule but not for MethodRule
329+
*/
330+
@Test
331+
public void runsMethodRuleThatIsReturnedByMethod() {
332+
wasRun = false;
333+
JUnitCore.runClasses(HasMethodReturningMethodRule.class);
334+
assertTrue(wasRun);
335+
}
336+
337+
public static class HasMultipleMethodsReturningMethodRule {
338+
@Rule
339+
public Increment methodRule1() {
340+
return new Increment();
341+
}
342+
343+
@Rule
344+
public Increment methodRule2() {
345+
return new Increment();
346+
}
347+
348+
@Test
349+
public void doNothing() {
350+
351+
}
352+
}
353+
354+
/**
355+
* If there are multiple public methods annotated with @Rule returning a {@link MethodRule}
356+
* then all the rules returned should be run.
357+
*
358+
* <p>This case has been added with
359+
* <a href="https://github.com/junit-team/junit/issues/589">Issue #589</a> -
360+
* Support @Rule for methods works only for TestRule but not for MethodRule
361+
*/
362+
@Test
363+
public void runsAllMethodRulesThatAreReturnedByMethods() {
364+
runCount = 0;
365+
JUnitCore.runClasses(HasMultipleMethodsReturningMethodRule.class);
366+
assertEquals(2, runCount);
367+
}
368+
369+
370+
public static class CallsMethodReturningRuleOnlyOnce {
371+
int callCount = 0;
372+
373+
private static class Dummy implements MethodRule {
374+
375+
@Override
376+
public Statement apply(final Statement base, FrameworkMethod method, Object target) {
377+
return new Statement() {
378+
379+
@Override
380+
public void evaluate() throws Throwable {
381+
base.evaluate();
382+
}
383+
};
384+
}
385+
};
386+
387+
388+
@Rule
389+
public MethodRule methodRule() {
390+
callCount++;
391+
return new Dummy();
392+
}
393+
394+
@Test
395+
public void doNothing() {
396+
assertEquals(1, callCount);
397+
}
398+
}
399+
400+
/**
401+
* If there are any public methods annotated with @Rule returning a {@link MethodRule}
402+
* then method should be called only once.
403+
*
404+
* <p>This case has been added with
405+
* <a href="https://github.com/junit-team/junit/issues/589">Issue #589</a> -
406+
* Support @Rule for methods works only for TestRule but not for MethodRule
407+
*/
408+
@Test
409+
public void callsMethodReturningRuleOnlyOnce() {
410+
assertTrue(JUnitCore.runClasses(CallsMethodReturningRuleOnlyOnce.class).wasSuccessful());
411+
}
295412
}

0 commit comments

Comments
 (0)