|
4 | 4 | */
|
5 | 5 | package org.mockito.testng;
|
6 | 6 |
|
| 7 | +import java.util.Map; |
| 8 | +import java.util.Optional; |
| 9 | +import java.util.WeakHashMap; |
| 10 | + |
| 11 | +import org.mockito.Mockito; |
7 | 12 | import org.mockito.MockitoSession;
|
8 |
| -import org.mockito.testng.internal.MockitoAfterTestNGMethod; |
9 |
| -import org.mockito.testng.internal.MockitoBeforeTestNGMethod; |
| 13 | +import org.mockito.quality.Strictness; |
10 | 14 | import org.testng.IInvokedMethod;
|
11 | 15 | import org.testng.IInvokedMethodListener;
|
12 | 16 | import org.testng.ITestNGListener;
|
13 | 17 | import org.testng.ITestResult;
|
14 | 18 | import org.testng.annotations.Listeners;
|
15 | 19 |
|
16 |
| -import java.util.Map; |
17 |
| -import java.util.WeakHashMap; |
18 |
| - |
19 | 20 | /**
|
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 | + * |
21 | 25 | * <ul>
|
22 | 26 | * <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> (@BeforeMethod, @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> (@BeforeMethod, @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} |
28 | 36 | * </li>
|
29 | 37 | * <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. |
35 | 39 | * </li>
|
36 | 40 | * </ul>
|
37 | 41 | *
|
38 |
| - * <p> |
39 |
| - * The listener is completely optional - there are other ways you can get @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 @Mock annotation in javadoc for {@link org.mockito.MockitoAnnotations} |
45 |
| - * |
| 42 | + * <p>Example usage:</p> |
46 | 43 | * <pre class="code"><code class="java">
|
47 | 44 | * <b>@Listeners(MockitoTestNGListener.class)</b>
|
48 | 45 | * public class ExampleTest {
|
|
56 | 53 | * }
|
57 | 54 | * }
|
58 | 55 | * </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> |
59 | 61 | */
|
60 | 62 | public class MockitoTestNGListener implements IInvokedMethodListener {
|
61 | 63 |
|
62 | 64 | private final Map<Object, MockitoSession> sessions = new WeakHashMap<>();
|
63 |
| - private final MockitoBeforeTestNGMethod beforeTest = new MockitoBeforeTestNGMethod(sessions); |
64 |
| - private final MockitoAfterTestNGMethod afterTest = new MockitoAfterTestNGMethod(sessions); |
65 | 65 |
|
| 66 | + @Override |
66 | 67 | 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 | + ); |
69 | 75 | }
|
70 | 76 | }
|
71 | 77 |
|
| 78 | + @Override |
72 | 79 | 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())); |
75 | 83 | }
|
76 | 84 | }
|
77 | 85 |
|
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()) { |
80 | 88 | if (hasMockitoTestNGListener(clazz)) {
|
81 | 89 | return true;
|
82 | 90 | }
|
|
0 commit comments