1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3 * SPDX-License-Identifier: MIT
4 */
5 package com.qulice.pmd;
6
7 import com.qulice.pmd.rules.ProhibitPlainJunitAssertionsRule;
8 import org.hamcrest.Matchers;
9 import org.junit.jupiter.api.Test;
10
11 /**
12 * Test case for {@link PmdValidator} covering JUnit assertion rules and
13 * related test-class conventions.
14 *
15 * @since 0.25.0
16 */
17 final class PmdAssertionsTest {
18
19 /**
20 * Pattern using plain JUnit assertions.
21 */
22 private static final String PLAIN_ASSERTIONS =
23 "Avoid using Plain JUnit assertions";
24
25 /**
26 * PmdValidator can prohibit plain JUnit assertion in import block like
27 * import static org.junit.Assert.assert* import static
28 * junit.framework.Assert.assert*.
29 *
30 * <p>Custom Rule {@link ProhibitPlainJunitAssertionsRule}</p>
31 *
32 * @throws Exception If something wrong happens inside.
33 */
34 @Test
35 void prohibitsStaticImportsPlainAssertionsInTests() throws Exception {
36 new PmdAssert(
37 "PlainJUnitAssertionStaticImportBlock.java",
38 Matchers.is(false),
39 Matchers.containsString(
40 PmdAssertionsTest.PLAIN_ASSERTIONS
41 )
42 ).assertOk();
43 }
44
45 /**
46 * PmdValidator can prohibit plain JUnit assertion in test methods like
47 * Assert.assertEquals.
48 *
49 * <p>Custom Rule {@link ProhibitPlainJunitAssertionsRule}</p>
50 *
51 * @throws Exception If something wrong happens inside.
52 */
53 @Test
54 void prohibitsPlainJunitAssertionsInTestMethods() throws Exception {
55 new PmdAssert(
56 "PlainJUnitAssertionTestMethod.java",
57 Matchers.is(false),
58 Matchers.containsString(
59 PmdAssertionsTest.PLAIN_ASSERTIONS
60 )
61 ).assertOk();
62 }
63
64 /**
65 * PmdValidator can allow Assert.fail().
66 *
67 * <p>Custom Rule {@link ProhibitPlainJunitAssertionsRule}</p>
68 *
69 * @throws Exception If something wrong happens inside.
70 */
71 @Test
72 void allowsAssertFail() throws Exception {
73 new PmdAssert(
74 "AllowAssertFail.java",
75 Matchers.is(false),
76 Matchers.allOf(
77 Matchers.not(
78 Matchers.containsString(
79 PmdAssertionsTest.PLAIN_ASSERTIONS
80 )
81 ),
82 Matchers.containsString("UnitTestContainsTooManyAsserts")
83 )
84 ).assertOk();
85 }
86
87 /**
88 * PmdValidator does not report UnitTestContainsTooManyAsserts when a test
89 * wraps an Assertions.assertThrows call inside an assertThat to verify the
90 * thrown exception's message.
91 *
92 * @throws Exception If something wrong happens inside.
93 */
94 @Test
95 void allowsAssertThrowsInsideAssertThat() throws Exception {
96 new PmdAssert(
97 "AssertThrowsWithMessageCheck.java",
98 Matchers.is(true),
99 Matchers.not(
100 Matchers.containsString("UnitTestContainsTooManyAsserts")
101 )
102 ).assertOk();
103 }
104
105 /**
106 * PmdValidator still reports UnitTestContainsTooManyAsserts when a test
107 * has multiple asserts in addition to an assertThrows call, because only
108 * assertThrows is excluded from the count.
109 *
110 * @throws Exception If something wrong happens inside.
111 */
112 @Test
113 void reportsTooManyAssertsEvenWithAssertThrows() throws Exception {
114 new PmdAssert(
115 "TooManyAssertsWithAssertThrows.java",
116 Matchers.is(false),
117 Matchers.containsString("UnitTestContainsTooManyAsserts")
118 ).assertOk();
119 }
120
121 /**
122 * PmdValidator does not report UnitTestContainsTooManyAsserts when a test
123 * has a single assertion combined with calls such as
124 * {@code Mockito.verify(...)} or {@code pull.checks()} whose simple name
125 * starts with 'verify' or 'check' but which are not JUnit/Hamcrest
126 * assertions.
127 * Regression test for https://github.com/yegor256/qulice/issues/1606
128 *
129 * @throws Exception If something wrong happens inside.
130 */
131 @Test
132 void allowsMockitoVerifyAndChecksCalls() throws Exception {
133 new PmdAssert(
134 "AssertThatWithMockitoVerifyAndChecks.java",
135 Matchers.any(Boolean.class),
136 Matchers.not(
137 Matchers.containsString("UnitTestContainsTooManyAsserts")
138 )
139 ).assertOk();
140 }
141
142 /**
143 * PmdValidator can allow only package private methods, marked with: Test,
144 * RepeatedTest, TestFactory, TestTemplate or ParameterizedTest annotations.
145 *
146 * @throws Exception If something wrong happens inside.
147 */
148 @Test
149 void requiresPackagePrivateTestMethods() throws Exception {
150 new PmdAssert(
151 "TestShouldBePackagePrivate.java",
152 Matchers.is(false),
153 Matchers.containsString("JUnitJupiterTestShouldBePackagePrivate")
154 ).assertOk();
155 }
156
157 /**
158 * PmdValidator can allow only final JUnit3 test classes.
159 *
160 * @throws Exception If something wrong happens inside.
161 */
162 @Test
163 void allowJunitThirdTestClassToBeFinal() throws Exception {
164 new PmdAssert(
165 "Junit3TestClassShouldBeFinal.java",
166 Matchers.is(false),
167 Matchers.containsString("JUnitTestClassShouldBeFinal")
168 ).assertOk();
169 }
170
171 /**
172 * PmdValidator can allow only final JUnit4 test classes.
173 *
174 * @throws Exception If something wrong happens inside.
175 */
176 @Test
177 void allowJunitFourthTestClassToBeFinal() throws Exception {
178 new PmdAssert(
179 "Junit4TestClassShouldBeFinal.java",
180 Matchers.is(false),
181 Matchers.containsString("JUnitTestClassShouldBeFinal")
182 ).assertOk();
183 }
184
185 /**
186 * PmdValidator can allow only final JUnit5 test classes.
187 *
188 * @throws Exception If something wrong happens inside.
189 */
190 @Test
191 void allowJunitFifthTestClassToBeFinal() throws Exception {
192 new PmdAssert(
193 "Junit5TestClassShouldBeFinal.java",
194 Matchers.is(false),
195 Matchers.containsString("JUnitTestClassShouldBeFinal")
196 ).assertOk();
197 }
198
199 /**
200 * PmdValidator can allow only final Junit test classes.
201 *
202 * @throws Exception If something wrong happens inside.
203 */
204 @Test
205 void allowJunitTestClassToBeFinal() throws Exception {
206 new PmdAssert(
207 "JunitTestClassIsFinal.java",
208 Matchers.is(false),
209 Matchers.allOf(
210 Matchers.not(
211 Matchers.containsString("JUnitTestClassShouldBeFinal")
212 ),
213 Matchers.containsString("UnitTestShouldIncludeAssert")
214 )
215 ).assertOk();
216 }
217
218 /**
219 * PmdValidator can find assert() calls placed inside a lambda
220 * body and not report UnitTestShouldIncludeAssert violation.
221 *
222 * @throws Exception If something wrong happens inside.
223 */
224 @Test
225 void findsAssertionInsideLambdaBody() throws Exception {
226 new PmdAssert(
227 "AssertInsideLambda.java",
228 Matchers.is(true),
229 Matchers.not(
230 Matchers.containsString("UnitTestShouldIncludeAssert")
231 )
232 ).assertOk();
233 }
234
235 /**
236 * PmdValidator recognises the cactoos-matchers convention of a
237 * no-argument {@code affirm()} call on an {@code Assertion} object as a
238 * valid assertion and does not report UnitTestShouldIncludeAssert.
239 * Regression test for https://github.com/yegor256/qulice/issues/1698
240 *
241 * @throws Exception If something wrong happens inside.
242 */
243 @Test
244 void findsAssertionInAffirmCall() throws Exception {
245 new PmdAssert(
246 "AssertionAffirm.java",
247 Matchers.is(true),
248 Matchers.not(
249 Matchers.containsString("UnitTestShouldIncludeAssert")
250 )
251 ).assertOk();
252 }
253
254 /**
255 * PmdValidator does not flag classes that use non-JUnit test conventions
256 * like g4s8/oot, where a class ends with {@code *Test} but uses a
257 * {@code public static void test()} entry point instead of
258 * {@code @Test}-annotated methods.
259 * Regression test for https://github.com/yegor256/qulice/issues/1064
260 *
261 * @throws Exception If something wrong happens inside.
262 */
263 @Test
264 void allowsNonJunitTestClassesWithStaticTestMethod() throws Exception {
265 new PmdAssert(
266 "OotStyleTest.java",
267 Matchers.any(Boolean.class),
268 Matchers.allOf(
269 Matchers.not(
270 Matchers.containsString("TestClassWithoutTestCases")
271 ),
272 Matchers.not(
273 Matchers.containsString(
274 "UnitTestShouldUseTestAnnotation"
275 )
276 )
277 )
278 ).assertOk();
279 }
280
281 /**
282 * PmdValidator does not report JUnitAssertionsShouldIncludeMessage
283 * (a.k.a. UnitTestAssertionsShouldIncludeMessage) when a test uses the
284 * Hamcrest two-argument form
285 * {@link org.hamcrest.MatcherAssert#assertThat(String, boolean)} where
286 * the first argument already is the message.
287 * Regression test for https://github.com/yegor256/qulice/issues/1315
288 *
289 * @throws Exception If something wrong happens inside.
290 */
291 @Test
292 void allowsMatcherAssertThatWithBooleanAndMessage() throws Exception {
293 new PmdAssert(
294 "MatcherAssertBooleanWithMessage.java",
295 Matchers.any(Boolean.class),
296 Matchers.not(
297 Matchers.containsString(
298 "UnitTestAssertionsShouldIncludeMessage"
299 )
300 )
301 ).assertOk();
302 }
303
304 /**
305 * PmdValidator still reports UnitTestAssertionsShouldIncludeMessage when
306 * the two-argument form of
307 * {@link org.hamcrest.MatcherAssert#assertThat(Object, org.hamcrest.Matcher)}
308 * is used without a reason. Guards the fix for
309 * https://github.com/yegor256/qulice/issues/1315 from over-suppressing.
310 *
311 * @throws Exception If something wrong happens inside.
312 */
313 @Test
314 void reportsMatcherAssertThatWithoutMessage() throws Exception {
315 new PmdAssert(
316 "MatcherAssertWithoutMessage.java",
317 Matchers.is(false),
318 Matchers.containsString(
319 "UnitTestAssertionsShouldIncludeMessage"
320 )
321 ).assertOk();
322 }
323 }