Skip to content

Commit

Permalink
Guard against multiple evaluations of before statement (mockito#1821)
Browse files Browse the repository at this point in the history
* Guard against multiple evaluations of before statement

Some rules evaluate the base statement multiple times, e.g. to execute
tests repeatedly. The changes made in mockito#1672 led to an exception in such
cases because the `MockitoListener` was registered multiple times. Now,
we only add the listener the first time the statement is evaluated in
order to restore the old behavior.

Fixes mockito#1767.

* Reset listener when removing it
  • Loading branch information
marcphilipp authored and epeee committed Jun 22, 2020
1 parent 3c86ed8 commit a8fbd72
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 9 deletions.
Expand Up @@ -36,11 +36,13 @@ protected Statement withBefores(FrameworkMethod method, final Object target, Sta
return new Statement() {
@Override
public void evaluate() throws Throwable {
// get new test listener and add it to the framework
mockitoTestListener = listenerSupplier.get();
Mockito.framework().addListener(mockitoTestListener);
// init annotated mocks before tests
MockitoAnnotations.initMocks(target);
if (mockitoTestListener == null) {
// get new test listener and add it to the framework
mockitoTestListener = listenerSupplier.get();
Mockito.framework().addListener(mockitoTestListener);
// init annotated mocks before tests
MockitoAnnotations.initMocks(target);
}
base.evaluate();
}
};
Expand All @@ -61,6 +63,7 @@ public void testFinished(Description description) throws Exception {
if (mockitoTestListener != null) {
Mockito.framework().removeListener(mockitoTestListener);
mockitoTestListener.testFinished(new DefaultTestFinishedEvent(target, description.getMethodName(), failure));
mockitoTestListener = null;
}
Mockito.validateMockitoUsage();
} catch (Throwable t) {
Expand Down
Expand Up @@ -9,11 +9,14 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.Statement;
import org.mockito.Mock;
import org.mockito.internal.junit.MockitoTestListener;
import org.mockito.internal.junit.TestFinishedEvent;
Expand All @@ -34,9 +37,7 @@ public void does_not_fail_when_tests_succeeds() throws Exception {
new DefaultInternalRunner(SuccessTest.class, supplier)
.run(newNotifier(runListener));

verify(runListener, never()).testFailure(any(Failure.class));
verify(runListener, times(1)).testFinished(any(Description.class));
verify(mockitoTestListener, only()).testFinished(any(TestFinishedEvent.class));
verifyTestFinishedSuccessfully();
}

@Test
Expand All @@ -53,6 +54,18 @@ public void does_not_fail_second_test_when_first_test_fail() throws Exception {
new DefaultInternalRunner(SuccessTest.class, supplier)
.run(newNotifier(runListener));

verifyTestFinishedSuccessfully();
}

@Test
public void does_not_fail_when_rule_invokes_statement_multiple_times() throws Exception {
new DefaultInternalRunner(TestWithRepeatingRule.class, supplier)
.run(newNotifier(runListener));

verifyTestFinishedSuccessfully();
}

private void verifyTestFinishedSuccessfully() throws Exception {
verify(runListener, never()).testFailure(any(Failure.class));
verify(runListener, times(1)).testFinished(any(Description.class));
verify(mockitoTestListener, only()).testFinished(any(TestFinishedEvent.class));
Expand All @@ -64,7 +77,7 @@ private RunNotifier newNotifier(RunListener listener) {
return notifier;
}

public static final class SuccessTest {
public static class SuccessTest {

@Test
public void this_test_is_NOT_supposed_to_fail() {
Expand All @@ -82,4 +95,16 @@ public void this_test_is_supposed_to_fail() {
assertNotNull(system);
}
}

public static final class TestWithRepeatingRule extends SuccessTest {

@Rule
public TestRule rule = (base, description) -> new Statement() {
@Override
public void evaluate() throws Throwable {
base.evaluate();
base.evaluate();
}
};
}
}

0 comments on commit a8fbd72

Please sign in to comment.