Skip to content

Commit ccbe77e

Browse files
Jami CogswellJami Cogswell
authored andcommitted
Java: move original files
1 parent 631ccdf commit ccbe77e

File tree

7 files changed

+145
-0
lines changed

7 files changed

+145
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# J-T-004: Non-static inner class defined in a JUnit 5 is missing a `@Nested` annotation
2+
3+
A non-static inner class defined in a JUnit 5 test missing a `@Nested` annotation will be excluded from execution and it may indicate a misunderstanding from the programmer.
4+
5+
## Overview
6+
7+
JUnit tests are grouped in a class, and starting from JUnit 5 users can group the test classes in a bigger class so they can share the local environment of the enclosing class. While this helps to organize the unit tests and foster code reuse, if the inner class is not annotated with `@Nested`, the unit tests in it will fail to execute during builds.
8+
9+
Note that static classes, whether static or not, behave as independent sets of unit tests. Thus, inner static classes do not share the information provided by the outer class with other inner classes. Thus, this rule only applies to non-static JUnit 5 inner classes.
10+
11+
## Recommendation
12+
13+
If you want the tests defined in an inner class to be recognized by the build plugin and be executed, annotate with `@Nested`, imported from `org.junit.jupiter.api`.
14+
15+
## Example
16+
17+
```java
18+
import org.junit.jupiter.api.Nested;
19+
import static org.junit.Assert.assertEquals;
20+
21+
public class IntegerOperationTest {
22+
private int i; // Shared variable among the inner classes.
23+
24+
@BeforeEach
25+
public void initTest() { i = 0; }
26+
27+
@Nested
28+
public class AdditionTest { // COMPLIANT: Inner test class annotated with `@Nested`.
29+
@Test
30+
public void addTest1() { // Test of an inner class, implying `AdditionTest` is a test class.
31+
assertEquals(1, i+1);
32+
}
33+
}
34+
35+
public class SubtractionTest { // NON_COMPLIANT: Inner test class missing `@Nested`.
36+
@Test
37+
public void addTest1() { // Test of an inner class, implying `SubtractionTest` is a test class.
38+
assertEquals(-1, i-1);
39+
}
40+
}
41+
42+
static public class MultiplicationTest { // COMPLIANT: static test class should not be annotated as `@Nested`.
43+
...
44+
}
45+
}
46+
```
47+
48+
## Implementation Notes
49+
50+
The `@Nested` annotation does not apply to inner static classes, since the meaning of the annotation is to mark a class as "a *non-static* inner class containing `@Test` methods to be picked up by a build system". Therefore, this rule does not aim to target static inner test classes with a `@Nested` annotation, nor does it try to enforce such correct usage of `@Nested`. Therefore, any code that resembles below is not non-compliant to this rule.
51+
52+
``` java
53+
@Nested
54+
public static class Test6 { // COMPLIANT: Although invalid, this matter is out of the scope
55+
@Test
56+
public void test() {
57+
}
58+
}
59+
```
60+
61+
## References
62+
63+
- JUnit: [Documentation on `@Nested`](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/Nested.html).
64+
- Baeldung: [JUnit 5 @Nested Test Classes](https://www.baeldung.com/junit-5-nested-test-classes).
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* @id java/junit5-non-static-inner-class-missing-nested-annotation
3+
* @name J-T-004: Non-static inner class defined in a JUnit5 test is missing a `@Nested` annotation
4+
* @description A non-static inner class defined in a JUnit5 test missing a `@Nested` annotation
5+
* will be excluded from execution and it may indicate a misunderstanding from the
6+
* programmer.
7+
* @kind problem
8+
* @precision very-high
9+
* @problem.severity warning
10+
* @tags maintainability
11+
* correctness
12+
*/
13+
14+
import java
15+
16+
class JUnit5TestClass extends Class {
17+
JUnit5TestClass() {
18+
this.getAMethod().getAnAnnotation().getType().hasQualifiedName("org.junit.jupiter.api", "Test")
19+
}
20+
}
21+
22+
from JUnit5TestClass testClass // `TestClass` by definition should have at least one @Test method.
23+
where
24+
not testClass.isStatic() and
25+
testClass instanceof InnerClass and // `InnerClass` is by definition a non-static nested class.
26+
not exists(Annotation annotation |
27+
annotation.getType().hasQualifiedName("org.junit.jupiter.api", "Nested") and
28+
annotation.getAnnotatedElement() = testClass
29+
)
30+
select testClass, "This JUnit5 inner test class lacks a @Nested annotation."
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import org.junit.jupiter.api.Test;
2+
import org.junit.jupiter.api.Nested;
3+
4+
public class AnnotationTest {
5+
@Nested
6+
public class Test1 { // COMPLIANT: Inner test class has `@Nested`
7+
@Test
8+
public void test() {
9+
}
10+
}
11+
12+
public class Test2 { // NON_COMPLIANT: Inner test class is missing a `@Nested`
13+
@Test
14+
public void test() {
15+
}
16+
}
17+
18+
public class Test3 { // COMPLIANT: Since it is empty, it is not a test class
19+
}
20+
21+
public class Test4 { // COMPLIANT: Since no methods have `@Test`, it is not a test class
22+
public void f() {
23+
}
24+
25+
public void g() {
26+
}
27+
28+
public void h() {
29+
}
30+
}
31+
32+
public static class Test5 { // COMPLIANT: Static inner test classes don't need `@Nested`
33+
@Test
34+
public void test() {
35+
}
36+
}
37+
38+
@Nested
39+
public static class Test6 { // COMPLIANT: Although invalid, this matter is out of the scope (see Implementation Details)
40+
@Test
41+
public void test() {
42+
}
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| AnnotationTest.java:12:16:12:20 | Test2 | This JUnit5 inner test class lacks a @Nested annotation. |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Likely Bugs/Frameworks/JUnit/JUnit5NonStaticInnerClassMissingNestedAnnotation.ql
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/junit-jupiter-api-5.2.0

java/ql/test/stubs/junit-jupiter-api-5.2.0/org/junit/jupiter/api/Nested.java

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)