Skip to content

Commit c725b9b

Browse files
Merge pull request #23 from mockito/fix-7
Start new MockitoSession for each test methods
2 parents d6c0a9d + 67c6d6b commit c725b9b

File tree

7 files changed

+114
-180
lines changed

7 files changed

+114
-180
lines changed

src/main/java/org/mockito/testng/MockitoTestNGListener.java

+40-32
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,42 @@
44
*/
55
package org.mockito.testng;
66

7+
import java.util.Map;
8+
import java.util.Optional;
9+
import java.util.WeakHashMap;
10+
11+
import org.mockito.Mockito;
712
import org.mockito.MockitoSession;
8-
import org.mockito.testng.internal.MockitoAfterTestNGMethod;
9-
import org.mockito.testng.internal.MockitoBeforeTestNGMethod;
13+
import org.mockito.quality.Strictness;
1014
import org.testng.IInvokedMethod;
1115
import org.testng.IInvokedMethodListener;
1216
import org.testng.ITestNGListener;
1317
import org.testng.ITestResult;
1418
import org.testng.annotations.Listeners;
1519

16-
import java.util.Map;
17-
import java.util.WeakHashMap;
18-
1920
/**
20-
* Mockito TestNG Listener, this listener adds the following behavior to your test :
21+
* <p>Mockito TestNG Listener, this listener initializes mocks and handles strict stubbing, it is similar to JUnit
22+
* <code>MockitoJUnitRunner</code>, <code>MockitoRule</code>, <code>MockitoExtension</code> and adds
23+
* the following behavior to your test:</p>
24+
*
2125
* <ul>
2226
* <li>
23-
* Initializes mocks annotated with {@link org.mockito.Mock}, so that <strong>explicit usage of
24-
* {@link org.mockito.MockitoAnnotations#initMocks(Object)} is not necessary</strong>.
25-
* <strong>Note :</strong> With TestNG, mocks are initialized before any TestNG method, either a <em>configuration
26-
* method</em> (&#064;BeforeMethod, &#064;BeforeClass, etc) or a <em>test</em> method, i.e. mocks are initialized
27-
* once only once for each test instance.
27+
* Before any TestNG method, either a <em>configuration method</em> (&#064;BeforeMethod, &#064;BeforeClass, etc)
28+
* or a <em>test</em> method MockitoSession is started by:
29+
* <pre class="code"><code class="java">
30+
* Mockito.mockitoSession()
31+
* .initMocks(testInstance)
32+
* .strictness(Strictness.STRICT_STUBS)
33+
* .startMocking()
34+
* </code></pre>
35+
* See javadoc {@link MockitoSession}
2836
* </li>
2937
* <li>
30-
* As mocks are initialized only once, they will be reset after each <em>test method</em>.
31-
* See javadoc {@link org.mockito.Mockito#reset(Object[])}
32-
* </li>
33-
* <li>
34-
* Validates framework usage after each test method. See javadoc for {@link org.mockito.Mockito#validateMockitoUsage()}.
38+
* After each <em>test</em> method {@link MockitoSession#finishMocking()} is called.
3539
* </li>
3640
* </ul>
3741
*
38-
* <p>
39-
* The listener is completely optional - there are other ways you can get &#064;Mock working, for example by writing a base class.
40-
* Explicitly validating framework usage is also optional because it is triggered automatically by Mockito every time you use the framework.
41-
* See javadoc for {@link org.mockito.Mockito#validateMockitoUsage()}.
42-
*
43-
* <p>
44-
* Read more about &#064;Mock annotation in javadoc for {@link org.mockito.MockitoAnnotations}
45-
*
42+
* <p>Example usage:</p>
4643
* <pre class="code"><code class="java">
4744
* <b>&#064;Listeners(MockitoTestNGListener.class)</b>
4845
* public class ExampleTest {
@@ -56,27 +53,38 @@
5653
* }
5754
* }
5855
* </code></pre>
56+
*
57+
* <p>
58+
* <code>MockitoTestNGListener</code> not working with parallel tests,
59+
* more information https://github.com/mockito/mockito-testng/issues/20
60+
* </p>
5961
*/
6062
public class MockitoTestNGListener implements IInvokedMethodListener {
6163

6264
private final Map<Object, MockitoSession> sessions = new WeakHashMap<>();
63-
private final MockitoBeforeTestNGMethod beforeTest = new MockitoBeforeTestNGMethod(sessions);
64-
private final MockitoAfterTestNGMethod afterTest = new MockitoAfterTestNGMethod(sessions);
6565

66+
@Override
6667
public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
67-
if (hasMockitoTestNGListenerInTestHierarchy(testResult.getTestClass().getRealClass())) {
68-
beforeTest.applyFor(method, testResult);
68+
if (hasMockitoTestNGListenerInTestHierarchy(testResult)) {
69+
sessions.computeIfAbsent(testResult.getInstance(), testInstance ->
70+
Mockito.mockitoSession()
71+
.initMocks(testInstance)
72+
.strictness(Strictness.STRICT_STUBS)
73+
.startMocking()
74+
);
6975
}
7076
}
7177

78+
@Override
7279
public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
73-
if (hasMockitoTestNGListenerInTestHierarchy(testResult.getTestClass().getRealClass())) {
74-
afterTest.applyFor(method, testResult);
80+
if (hasMockitoTestNGListenerInTestHierarchy(testResult) && method.isTestMethod()) {
81+
Optional.ofNullable(sessions.remove(testResult.getInstance()))
82+
.ifPresent(mockitoSession -> mockitoSession.finishMocking(testResult.getThrowable()));
7583
}
7684
}
7785

78-
protected boolean hasMockitoTestNGListenerInTestHierarchy(Class<?> testClass) {
79-
for (Class<?> clazz = testClass; clazz != Object.class; clazz = clazz.getSuperclass()) {
86+
protected boolean hasMockitoTestNGListenerInTestHierarchy(ITestResult testResult) {
87+
for (Class<?> clazz = testResult.getTestClass().getRealClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
8088
if (hasMockitoTestNGListener(clazz)) {
8189
return true;
8290
}

src/main/java/org/mockito/testng/internal/MockitoAfterTestNGMethod.java

-52
This file was deleted.

src/main/java/org/mockito/testng/internal/MockitoBeforeTestNGMethod.java

-73
This file was deleted.

src/test/java/org/mockitousage/testng/StrictStubsTest.java

+22-9
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,31 @@
44
*/
55
package org.mockitousage.testng;
66

7+
import java.util.List;
8+
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
import static org.mockito.Mockito.verifyNoMoreInteractions;
11+
import static org.mockito.Mockito.when;
12+
713
import org.assertj.core.api.Assertions;
814
import org.mockito.Mock;
915
import org.mockito.exceptions.misusing.PotentialStubbingProblem;
1016
import org.mockito.exceptions.misusing.UnnecessaryStubbingException;
1117
import org.mockito.testng.MockitoTestNGListener;
1218
import org.mockitousage.testng.failuretests.HasUnusedStubs;
19+
import org.mockitousage.testng.failuretests.HasUnusedStubsInSetup;
1320
import org.mockitousage.testng.utils.TestNGRunner;
1421
import org.testng.annotations.Listeners;
1522
import org.testng.annotations.Test;
1623

17-
import java.util.List;
18-
19-
import static org.assertj.core.api.Assertions.assertThat;
20-
import static org.mockito.Mockito.verifyNoMoreInteractions;
21-
import static org.mockito.Mockito.when;
22-
2324
@Listeners(MockitoTestNGListener.class)
2425
public class StrictStubsTest {
2526

26-
@Mock List list;
27+
@Mock
28+
List<String> list;
2729

28-
@Test public void detects_potential_stubbing_problem() {
30+
@Test
31+
public void detects_potential_stubbing_problem() {
2932
when(list.add("a")).thenReturn(true);
3033

3134
Assertions.assertThatThrownBy(() -> StrictStubsCode.testStrictStubs(list, "b"))
@@ -40,7 +43,17 @@ public class StrictStubsTest {
4043
assertThat(result.getFailure()).isInstanceOf(UnnecessaryStubbingException.class);
4144
}
4245

43-
@Test public void keeps_tests_dry() {
46+
@Test
47+
public void detects_unused_stubs_in_setup() {
48+
//when
49+
TestNGRunner.Result result = new TestNGRunner().run(HasUnusedStubsInSetup.class);
50+
51+
//then
52+
assertThat(result.getFailure()).isInstanceOf(UnnecessaryStubbingException.class);
53+
}
54+
55+
@Test
56+
public void keeps_tests_dry() {
4457
//when
4558
when(list.add("a")).thenReturn(true);
4659
list.add("a");

src/test/java/org/mockitousage/testng/failuretests/FailingOnPurposeBecauseIncorrectStubbingSyntax.java

+7-11
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@
44
*/
55
package org.mockitousage.testng.failuretests;
66

7-
import org.mockito.exceptions.misusing.InvalidUseOfMatchersException;
8-
import org.mockito.testng.MockitoTestNGListener;
9-
import org.testng.annotations.Listeners;
10-
import org.testng.annotations.Test;
7+
import static org.mockito.ArgumentMatchers.anySet;
8+
import static org.mockito.ArgumentMatchers.anyString;
9+
import static org.mockito.Mockito.mock;
1110

1211
import java.io.PrintStream;
1312

14-
import static org.mockito.Matchers.anySet;
15-
import static org.mockito.Matchers.anyString;
16-
import static org.mockito.Mockito.mock;
13+
import org.mockito.testng.MockitoTestNGListener;
14+
import org.testng.annotations.Listeners;
15+
import org.testng.annotations.Test;
1716

1817
/**
1918
* Should fail.
@@ -24,10 +23,7 @@
2423
@Test(description = "Always failing, shouldn't be listed in 'mockito-testng.xml'")
2524
public class FailingOnPurposeBecauseIncorrectStubbingSyntax {
2625

27-
@SuppressWarnings("CheckReturnValue")
28-
@Test(expectedExceptions = InvalidUseOfMatchersException.class)
29-
public void incorrect_stubbing_syntax_in_test() throws Exception {
30-
mock(PrintStream.class);
26+
public void incorrect_stubbing_syntax_in_test() {
3127
anyString();
3228
anySet();
3329
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.mockitousage.testng.failuretests;
2+
3+
import java.util.List;
4+
5+
import static org.assertj.core.api.Assertions.assertThat;
6+
import static org.mockito.Mockito.when;
7+
8+
import org.mockito.Mock;
9+
import org.mockito.testng.MockitoTestNGListener;
10+
import org.testng.annotations.BeforeMethod;
11+
import org.testng.annotations.Listeners;
12+
import org.testng.annotations.Test;
13+
14+
@Listeners(MockitoTestNGListener.class)
15+
public class HasUnusedStubsInSetup {
16+
17+
@Mock
18+
List<String> mock;
19+
20+
@BeforeMethod
21+
public void setup() {
22+
when(mock.add("a")).thenReturn(true);
23+
}
24+
25+
@Test(priority = 1)
26+
public void testPass() {
27+
assertThat(mock.add("a")).isTrue();
28+
}
29+
30+
@Test(priority = 2)
31+
public void testFail() {
32+
// stub from setup is not used
33+
}
34+
35+
}

0 commit comments

Comments
 (0)