[java] UnitTestShouldUseAfterAnnotation: Consider JUnit 5 and TestNG

This commit is contained in:
Andreas Dangel 2024-10-03 17:18:04 +02:00
parent 33c737718c
commit 9337e5a7a2
No known key found for this signature in database
GPG Key ID: 93450DF2DF9A3FA3
3 changed files with 163 additions and 97 deletions

View File

@ -17,6 +17,7 @@ This is a {{ site.pmd.release_type }} release.
### 🌟 Rule Changes
#### Changed Rules
* {% rule java/bestpractices/UnitTestShouldUseAfterAnnotation %} (Java Best Practices) now also considers JUnit 5 and TestNG tests.
* {% rule java/bestpractices/UnitTestShouldUseBeforeAnnotation %} (Java Best Practices) now also considers JUnit 5 and TestNG tests.
#### Renamed Rules

View File

@ -1295,36 +1295,46 @@ public class MyTestCase {
<rule name="UnitTestShouldUseAfterAnnotation"
language="java"
since="4.0"
message="JUnit 4 tests that clean up tests should use the @After annotation, JUnit5 tests should use @AfterEach or @AfterAll"
message="Apply the correct annotation if this method is used to clean up the tests"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#unittestshoulduseafterannotation">
<description>
This rule detects methods called `tearDown()` that are not properly annotated as a cleanup method.
This is primarily intended to assist in upgrading from JUnit 3, where tear down methods were required to be called `tearDown()`.
To a lesser extent, this may help detect omissions under newer JUnit versions, as long as you are following this convention to name the methods.
This rule detects methods called `tearDown()` that are not properly annotated as a cleanup method.
This is primarily intended to assist in upgrading from JUnit 3, where tear down methods were required to be called `tearDown()`.
To a lesser extent, this may help detect omissions even under newer JUnit versions or under TestNG,
as long as you are following this convention to name the methods.
JUnit 4 will only execute methods annotated with `@After` after running each test.
JUnit 5 introduced `@AfterEach` and `@AfterAll` annotations to execute methods after each test or after all tests in the class, respectively.
* JUnit 4 will only execute methods annotated with `@After` after running each test.
* JUnit 5 introduced `@AfterEach` and `@AfterAll` annotations to execute methods after each test or after
all tests in the class, respectively.
* TestNG provides the annotations `@AfterMethod` and `@AfterClass` to execute methods after each test or after
tests in the class, respectively.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
<![CDATA[
//MethodDeclaration[@Name='tearDown' and @Arity=0]
[not(ModifierList/Annotation[
pmd-java:typeIs('org.junit.After')
or pmd-java:typeIs('org.junit.jupiter.api.AfterEach')
or pmd-java:typeIs('org.junit.jupiter.api.AfterAll')
or pmd-java:typeIs('org.testng.annotations.AfterMethod')])]
(: Make sure this is a junit 4 class :)
[../MethodDeclaration[pmd-java:hasAnnotation('org.junit.Test')]]
or pmd-java:typeIs('org.testng.annotations.AfterClass')
or pmd-java:typeIs('org.testng.annotations.AfterMethod')
])]
(: Make sure this is a JUnit 4/5 or TestNG class :)
[../MethodDeclaration[
pmd-java:hasAnnotation('org.junit.Test')
or pmd-java:hasAnnotation('org.junit.jupiter.api.Test')
or pmd-java:hasAnnotation('org.testng.annotations.Test')
]]
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
<![CDATA[
public class MyTest {
public void tearDown() {
bad();

View File

@ -5,129 +5,69 @@
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
<test-code>
<description>Contains tearDown</description>
<description>JUnit4 test class contains tearDown</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>3</expected-linenumbers>
<code><![CDATA[
import org.junit.Test;
public class Foo {
public void tearDown() {
}
public void tearDown() {}
@Test
public void foo() {
}
public void foo() {}
}
]]></code>
</test-code>
<test-code>
<description>Contains @After tearDown</description>
<description>JUnit4 test class contains tearDown with different signature is ok</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.junit.Test;
public class Foo {
public void tearDown(int something) {}
@Test
public void foo() {}
}
]]></code>
</test-code>
<test-code>
<description>JUnit4 test class contains @After tearDown is ok</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.junit.Test;
import org.junit.After;
public class Foo {
@After
public void tearDown() {
}
public void tearDown() {}
@Test
public void foo() {
}
public void foo() {}
}
]]></code>
</test-code>
<test-code>
<description>Renamed tearDown</description>
<description>JUnit4 test class contains renamed tearDown is ok</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.junit.Test;
import org.junit.After;
public class Foo {
@After
public void clean() {
}
public void clean() {}
@Test
public void foo() {
}
public void foo() {}
}
]]></code>
</test-code>
<test-code>
<description>#1446 False positive with JUnit4TestShouldUseBeforeAnnotation when TestNG is used</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
public class Foo {
@AfterMethod
public void tearDown(java.lang.reflect.Method m) {
//...
}
@Test
public void someTest() {}
}
]]></code>
</test-code>
<test-code>
<description>#940 False positive with JUnit4TestShouldUseAfterAnnotation when JUnit5's 'AfterEach' is used</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.junit.jupiter.api.*;
public class Foo {
@AfterEach
public void tearDown() {
//...
}
@Test
public void someTest() {}
}
]]></code>
</test-code>
<test-code>
<description>#940 False positive with JUnit4TestShouldUseAfterAnnotation when JUnit5's 'AfterAll' is used</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.junit.jupiter.api.*;
public class Foo {
@AfterAll
public void tearDown() {
//...
}
@Test
public void someTest() {}
}
]]></code>
</test-code>
<test-code>
<description>Contains tearDown, not a junit 4 test</description>
<description>Contains tearDown, not a JUnit 4/5 or TestNG test is ok</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
public void tearDown() {
}
public void foo() {
}
}
]]></code>
</test-code>
<test-code>
<description>Contains tearDown with different signature</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.junit.Test;
public class Foo {
public void tearDown(int something) {
}
@Test
public void foo() {
}
public void tearDown() {}
public void foo() {}
}
]]></code>
</test-code>
@ -151,4 +91,119 @@ public class AReallyCoolFeatureTest extends BaseTest {
}
]]></code>
</test-code>
<test-code>
<description>TestNG test contains tearDown</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>4</expected-linenumbers>
<code><![CDATA[
import org.testng.annotations.Test;
public class MyTestCase {
public void tearDown() {} // violation expected
@Test
public void myTest() {}
}
]]></code>
</test-code>
<test-code>
<description>TestNG test contains tearDown with different signature is ok (#1446)</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
public class Foo {
@AfterMethod
public void tearDown(java.lang.reflect.Method m) {}
@Test
public void foo() {}
}
]]></code>
</test-code>
<test-code>
<description>TestNG test contains tearDown with @AfterMethod is ok</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
public class MyTestCase {
@AfterMethod
public void tearDown() {}
@Test
public void myTest() {}
}
]]></code>
</test-code>
<test-code>
<description>TestNG test contains tearDown with @AfterClass is ok</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;
public class MyTestCase {
@AfterClass
public void tearDown() {}
@Test
public void myTest() {}
}
]]></code>
</test-code>
<test-code>
<description>JUnit 5 test class contains tearDown</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>4</expected-linenumbers>
<code><![CDATA[
import org.junit.jupiter.api.Test;
public class MyTestCase {
public void tearDown() {} // violation expected
@Test
public void myTest() {}
}
]]></code>
</test-code>
<test-code>
<description>JUnit 5 test class contains tearDown with @AfterEach is ok (#940)</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
public class MyTestCase {
@AfterEach
public void tearDown() {}
@Test
public void myTest() {}
}
]]></code>
</test-code>
<test-code>
<description>JUnit 5 test class contains tearDown with @AfterAll is ok (#940)</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
public class MyTestCase {
@AfterAll
public void tearDown() {}
@Test
public void myTest() {}
}
]]></code>
</test-code>
</test-data>