Skip to content

Commit 565282b

Browse files
SONARJAVA-4424 add methods annotated with @test to the list of testNg test methods (#4953)
1 parent 889e741 commit 565282b

5 files changed

Lines changed: 94 additions & 4 deletions

File tree

its/autoscan/src/test/resources/autoscan/diffs/diff_S2187.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"ruleKey": "S2187",
33
"hasTruePositives": true,
44
"falseNegatives": 13,
5-
"falsePositives": 0
5+
"falsePositives": 1
66
}

java-checks-test-sources/default/src/main/files/non-compiling/checks/NoTestInTestClassCheckSample.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,33 @@ public class TestNGClassTest { // Noncompliant
121121
private void test1() { }
122122
public static void foo() {}
123123
}
124+
125+
@org.testng.annotations.Test
126+
class TestNGClassTestWithMethodAnnotated { // compliant, with testng when the class is annotated with @Test all public methods are considered as tests
127+
// non public methods can also be added to tests with the @Test annotation
128+
@org.testng.annotations.Test
129+
void myMethod(){
130+
131+
}
132+
}
133+
134+
@org.testng.annotations.Test
135+
class TestNGClassWithUnkownAnnotation {
136+
@Unkown
137+
void myMethod(){
138+
139+
}
140+
}
141+
142+
@org.testng.annotations.Test
143+
class TestNGClassWithWrongAnnotation { // Noncompliant
144+
@Override
145+
void myMethod(){
146+
147+
}
148+
}
149+
150+
124151
@org.testng.annotations.Test(groups ="integration")
125152
public abstract class AbstractIntegrationTest2{
126153
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package checks.tests;
2+
3+
import org.testng.annotations.Test;
4+
5+
class TestNGTest {
6+
@Test
7+
void foo() {
8+
}
9+
}
10+
11+
@Test
12+
class TestNgFooTest {
13+
public void test1() {
14+
}
15+
16+
public void test2() {
17+
}
18+
}
19+
20+
@Test
21+
class TestNGClassTest { // Noncompliant
22+
public int field;
23+
private void test1() { }
24+
public static void foo() {}
25+
}
26+
27+
@Test
28+
class TestNGClassTestUseAnnotation {
29+
30+
@Test
31+
void myMethod(){
32+
33+
}
34+
}
35+
36+
@Test(groups ="integration")
37+
abstract class AbstractIntegrationTest2{
38+
}

java-checks/src/main/java/org/sonar/java/checks/tests/NoTestInTestClassCheck.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.List;
2323
import java.util.Optional;
2424
import java.util.Set;
25+
import java.util.function.Predicate;
2526
import java.util.regex.Pattern;
2627
import java.util.stream.Stream;
2728
import org.apache.commons.lang3.StringUtils;
@@ -44,6 +45,7 @@ public class NoTestInTestClassCheck extends IssuableSubscriptionVisitor {
4445
public static final String ARCH_UNIT_RUNNER = "ArchUnitRunner";
4546
public static final String ARCH_UNIT_ANALYZE_CLASSES = "com.tngtech.archunit.junit.AnalyzeClasses";
4647
public static final String ARCH_UNIT_TEST = "com.tngtech.archunit.junit.ArchTest";
48+
private static final String TEST_NG_TEST = "org.testng.annotations.Test";
4749

4850
private static final List<String> PACT_UNIT_TEST = Arrays.asList("au.com.dius.pact.provider.junit.State", "au.com.dius.pact.provider.junitsupport.State");
4951

@@ -75,7 +77,7 @@ public void visitNode(Tree tree) {
7577

7678
private void resetAnnotationCache() {
7779
Arrays.asList(testFieldAnnotations, testMethodAnnotations, seenAnnotations).forEach(Set::clear);
78-
testMethodAnnotations.addAll(Arrays.asList("org.junit.Test", "org.testng.annotations.Test", "org.junit.jupiter.api.Test"));
80+
testMethodAnnotations.addAll(Arrays.asList("org.junit.Test", TEST_NG_TEST, "org.junit.jupiter.api.Test"));
7981
}
8082

8183
private void checkClass(ClassTree classTree) {
@@ -93,7 +95,7 @@ private void checkClass(ClassTree classTree) {
9395
Symbol.TypeSymbol classSymbol = classTree.symbol();
9496
Stream<Symbol> members = getAllMembers(classSymbol, checkRunWith(classSymbol, "Enclosed"));
9597
IdentifierTree simpleName = classTree.simpleName();
96-
if (classSymbol.metadata().isAnnotatedWith("org.testng.annotations.Test")) {
98+
if (classSymbol.metadata().isAnnotatedWith(TEST_NG_TEST)) {
9799
checkTestNGmembers(simpleName, members);
98100
} else {
99101
boolean isJunit3TestClass = classSymbol.type().isSubtypeOf("junit.framework.TestCase");
@@ -124,7 +126,22 @@ private static boolean isArchUnitTestClass(Symbol.TypeSymbol classSymbol) {
124126
}
125127

126128
private void checkTestNGmembers(IdentifierTree className, Stream<Symbol> members) {
127-
if (members.noneMatch(member -> member.isMethodSymbol() && member.isPublic() && !member.isStatic() && !"<init>".equals(member.name()))) {
129+
Predicate<SymbolMetadata.AnnotationInstance> isTestNgAnnotation = ann -> {
130+
Type type = ann.symbol().type();
131+
return type.isUnknown() || type.is(TEST_NG_TEST);
132+
};
133+
Predicate<Symbol> isTestMethod = member -> {
134+
if(!member.isMethodSymbol()){
135+
return false;
136+
}
137+
138+
// we know member is a method.
139+
boolean annotatedWithTest = member.metadata().annotations().stream().anyMatch(isTestNgAnnotation);
140+
boolean publicMethod = member.isPublic() && !member.isStatic() && !"<init>".equals(member.name());
141+
return annotatedWithTest || publicMethod;
142+
};
143+
144+
if (members.noneMatch(isTestMethod)) {
128145
reportClass(className);
129146
}
130147
}

java-checks/src/test/java/org/sonar/java/checks/tests/NoTestInTestClassCheckTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,12 @@ void pactUnit() {
8585
.withCheck(new NoTestInTestClassCheck())
8686
.verifyIssues();
8787
}
88+
89+
@Test
90+
void testNg() {
91+
CheckVerifier.newVerifier()
92+
.onFile(testCodeSourcesPath("checks/tests/NoTestInTestClassCheckTestNgTest.java"))
93+
.withCheck(new NoTestInTestClassCheck())
94+
.verifyIssues();
95+
}
8896
}

0 commit comments

Comments
 (0)