Skip to content

Commit 442aac5

Browse files
Merge pull request #27 from mockito/fix-10
Possibility to configure strictness level
2 parents 8ab741f + 74c4dbb commit 442aac5

File tree

6 files changed

+210
-29
lines changed

6 files changed

+210
-29
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (c) 2020 Mockito contributors
3+
* This program is made available under the terms of the MIT License.
4+
*/
5+
package org.mockito.testng;
6+
7+
import java.lang.annotation.Documented;
8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.Inherited;
10+
import java.lang.annotation.Retention;
11+
import java.lang.annotation.RetentionPolicy;
12+
import java.lang.annotation.Target;
13+
14+
import org.mockito.quality.Strictness;
15+
16+
/**
17+
* Annotation that can configure Mockito settings. Used by {@link MockitoTestNGListener}
18+
*/
19+
@Inherited
20+
@Retention(RetentionPolicy.RUNTIME)
21+
@Target({ElementType.TYPE})
22+
@Documented
23+
public @interface MockitoSettings {
24+
25+
/**
26+
* Configure the strictness used in this test.
27+
*
28+
* @return The strictness to configure, by default {@link Strictness#STRICT_STUBS}
29+
*/
30+
Strictness strictness() default Strictness.STRICT_STUBS;
31+
}

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

+60-28
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@
44
*/
55
package org.mockito.testng;
66

7+
import java.lang.annotation.Annotation;
8+
import java.util.Arrays;
79
import java.util.Map;
810
import java.util.Optional;
911
import java.util.WeakHashMap;
12+
import java.util.stream.Stream;
1013

1114
import org.mockito.Mockito;
1215
import org.mockito.MockitoSession;
1316
import org.mockito.quality.Strictness;
1417
import org.testng.IInvokedMethod;
1518
import org.testng.IInvokedMethodListener;
16-
import org.testng.ITestNGListener;
19+
import org.testng.ITestNGMethod;
1720
import org.testng.ITestResult;
1821
import org.testng.annotations.Listeners;
1922

@@ -55,6 +58,19 @@
5558
* </code></pre>
5659
*
5760
* <p>
61+
* By default {@link MockitoSession} is started with {@link Strictness#STRICT_STUBS}.
62+
* You can change this behavior by adding {@link MockitoSettings} to your test class.
63+
* </p>
64+
*
65+
* <pre class="code"><code class="java">
66+
* <b>&#064;Listeners(MockitoTestNGListener.class)</b>
67+
* <b>&#064;MockitoSettings(strictness = Strictness.WARN)</b>
68+
* public class ExampleTest {
69+
* ...
70+
* }
71+
* </code></pre>
72+
*
73+
* <p>
5874
* <code>MockitoTestNGListener</code> not working with parallel tests,
5975
* more information https://github.com/mockito/mockito-testng/issues/20
6076
* </p>
@@ -65,49 +81,65 @@ public class MockitoTestNGListener implements IInvokedMethodListener {
6581

6682
@Override
6783
public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
68-
if (hasMockitoTestNGListenerInTestHierarchy(testResult)) {
69-
sessions.computeIfAbsent(testResult.getInstance(), testInstance ->
70-
Mockito.mockitoSession()
71-
.initMocks(testInstance)
72-
.strictness(Strictness.STRICT_STUBS)
73-
.startMocking()
84+
if (shouldBeRunBeforeInvocation(method, testResult)) {
85+
sessions.computeIfAbsent(testResult.getInstance(), testInstance -> {
86+
87+
Strictness strictness = findAnnotation(testResult, MockitoSettings.class)
88+
.map(MockitoSettings::strictness).orElse(Strictness.STRICT_STUBS);
89+
90+
return Mockito.mockitoSession()
91+
.initMocks(testInstance)
92+
.strictness(strictness)
93+
.startMocking();
94+
}
7495
);
7596
}
7697
}
7798

7899
@Override
79100
public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
80-
if (hasMockitoTestNGListenerInTestHierarchy(testResult) && method.isTestMethod()) {
101+
if (shouldBeRunAfterInvocation(method, testResult)) {
81102
Optional.ofNullable(sessions.remove(testResult.getInstance()))
82103
.ifPresent(mockitoSession -> mockitoSession.finishMocking(testResult.getThrowable()));
83104
}
84105
}
85106

86-
protected boolean hasMockitoTestNGListenerInTestHierarchy(ITestResult testResult) {
87-
for (Class<?> clazz = testResult.getTestClass().getRealClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
88-
if (hasMockitoTestNGListener(clazz)) {
89-
return true;
90-
}
91-
}
92-
return false;
107+
private boolean shouldBeRunBeforeInvocation(IInvokedMethod method, ITestResult testResult) {
108+
return !isAfterConfigurationMethod(method) && hasMockitoTestNGListener(testResult);
93109
}
94110

95-
protected boolean hasMockitoTestNGListener(Class<?> clazz) {
96-
Listeners listeners = clazz.getAnnotation(Listeners.class);
97-
if (listeners == null) {
98-
return false;
99-
}
111+
private boolean isAfterConfigurationMethod(IInvokedMethod method) {
112+
ITestNGMethod testMethod = method.getTestMethod();
113+
return testMethod.isAfterClassConfiguration()
114+
|| testMethod.isAfterMethodConfiguration()
115+
|| testMethod.isAfterGroupsConfiguration()
116+
|| testMethod.isAfterTestConfiguration()
117+
|| testMethod.isAfterSuiteConfiguration();
118+
}
100119

101-
for (Class<? extends ITestNGListener> listenerClass : listeners.value()) {
102-
if (listenerClass() == listenerClass) {
103-
return true;
104-
}
105-
}
106-
return false;
120+
private boolean shouldBeRunAfterInvocation(IInvokedMethod method, ITestResult testResult) {
121+
return method.isTestMethod() && hasMockitoTestNGListener(testResult);
107122
}
108123

109-
protected Class<MockitoTestNGListener> listenerClass() {
110-
return MockitoTestNGListener.class;
124+
protected boolean hasMockitoTestNGListener(ITestResult testResult) {
125+
126+
return findAnnotation(testResult, Listeners.class)
127+
.map(Listeners::value)
128+
.map(Arrays::stream)
129+
.orElseGet(Stream::empty)
130+
.anyMatch(listener -> listener == MockitoTestNGListener.class);
111131
}
112132

133+
<A extends Annotation> Optional<A> findAnnotation(ITestResult testResult, Class<A> annotationClass) {
134+
135+
for (Class<?> clazz = testResult.getTestClass().getRealClass();
136+
clazz != Object.class; clazz = clazz.getSuperclass()) {
137+
Optional<A> annotation = Optional.ofNullable(clazz.getAnnotation(annotationClass));
138+
if (annotation.isPresent()) {
139+
return annotation;
140+
}
141+
}
142+
143+
return Optional.empty();
144+
}
113145
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright (c) 2020 Mockito contributors
3+
* This program is made available under the terms of the MIT License.
4+
*/
5+
package org.mockitousage.testng;
6+
7+
import static org.assertj.core.api.Assertions.assertThat;
8+
import static org.mockito.Mockito.times;
9+
import static org.mockito.Mockito.verify;
10+
import static org.mockito.Mockito.when;
11+
12+
import java.util.Random;
13+
14+
import org.mockito.InjectMocks;
15+
import org.mockito.Mock;
16+
import org.mockito.testng.MockitoTestNGListener;
17+
import org.testng.annotations.AfterMethod;
18+
import org.testng.annotations.Listeners;
19+
import org.testng.annotations.Test;
20+
21+
@Listeners(MockitoTestNGListener.class)
22+
public class InjectMocksWithConstructorAndFinalTest {
23+
24+
interface Client {
25+
int aMethod();
26+
}
27+
28+
static class Service {
29+
final Client client;
30+
31+
public Service(Client client) {
32+
this.client = client;
33+
}
34+
35+
int callClient() {
36+
return client.aMethod();
37+
}
38+
}
39+
40+
@Mock
41+
private Client client;
42+
43+
@InjectMocks
44+
private Service service;
45+
46+
private int lastClientHash = 0;
47+
48+
@AfterMethod
49+
void cleanup() {
50+
// final field will be not assigned for next test
51+
// for new object constructor will be used
52+
service = null;
53+
}
54+
55+
@Test(invocationCount = 4)
56+
void inject_mock_should_be_refreshed() {
57+
58+
// we have correct injected mock
59+
assertThat(client).isNotNull();
60+
assertThat(service.client).isNotNull().isSameAs(client);
61+
62+
// we have new mock
63+
assertThat(lastClientHash).isNotEqualTo(client.hashCode());
64+
65+
// clear mock
66+
assertThat(service.callClient()).isZero();
67+
68+
// make some stub
69+
int i = new Random().nextInt() + 1;
70+
when(client.aMethod()).thenReturn(i);
71+
72+
// and test it
73+
assertThat(service.callClient()).isEqualTo(i);
74+
75+
verify(client, times(2)).aMethod();
76+
77+
// remember last mock hash
78+
lastClientHash = client.hashCode();
79+
}
80+
}

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

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.mockito.Mock;
1515
import org.mockito.exceptions.misusing.PotentialStubbingProblem;
1616
import org.mockito.exceptions.misusing.UnnecessaryStubbingException;
17+
import org.mockito.testng.MockitoSettings;
1718
import org.mockito.testng.MockitoTestNGListener;
1819
import org.mockitousage.testng.failuretests.HasUnusedStubs;
1920
import org.mockitousage.testng.failuretests.HasUnusedStubsInSetup;
@@ -22,6 +23,8 @@
2223
import org.testng.annotations.Test;
2324

2425
@Listeners(MockitoTestNGListener.class)
26+
// MockitoSettings with default values
27+
@MockitoSettings
2528
public class StrictStubsTest {
2629

2730
@Mock
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2020 Mockito contributors
3+
* This program is made available under the terms of the MIT License.
4+
*/
5+
package org.mockitousage.testng;
6+
7+
import static org.mockito.Mockito.when;
8+
9+
import java.util.List;
10+
11+
import org.mockito.Mock;
12+
import org.mockito.quality.Strictness;
13+
import org.mockito.testng.MockitoSettings;
14+
import org.mockito.testng.MockitoTestNGListener;
15+
import org.testng.annotations.BeforeMethod;
16+
import org.testng.annotations.Listeners;
17+
import org.testng.annotations.Test;
18+
19+
@Listeners(MockitoTestNGListener.class)
20+
@MockitoSettings(strictness = Strictness.WARN)
21+
public class StrictnessWarnTest {
22+
23+
@Mock
24+
private List<String> list;
25+
26+
@BeforeMethod
27+
void setup() {
28+
when(list.add("a")).thenReturn(true);
29+
}
30+
31+
@Test
32+
void not_used_stub_generate_warn() {
33+
//
34+
}
35+
}

version.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=0.1.*
1+
version=0.2.*

0 commit comments

Comments
 (0)