-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Java: Add new quality query to detect missing @Nested annotation in JUnit5 tests
#19094
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ccbe77e
f17e726
b08c8d0
ed57bc7
640096c
3e13f0e
4d7bed6
35b6478
37092f4
dca4c58
0f00262
b9bf192
e169c21
92cdddf
e458aca
faeb7ab
6ade978
77eeab3
02ded89
07a694e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| ## Overview | ||
|
|
||
| JUnit tests are grouped in a class, and starting from JUnit 5, users can group the test classes in a larger class so they can share the local environment of the enclosing class. While this helps organize the unit tests and foster code reuse, if an inner test class is not annotated with `@Nested`, the unit tests in it will fail to execute during builds. | ||
|
|
||
| ## Recommendation | ||
|
|
||
| If you want the tests defined in an inner class to be recognized by the build plugin and be executed, annotate the class with `@Nested`, imported from `org.junit.jupiter.api`. | ||
|
|
||
| ## Example | ||
|
|
||
| ```java | ||
| import org.junit.jupiter.api.Nested; | ||
| import static org.junit.Assert.assertEquals; | ||
|
|
||
| public class IntegerOperationTest { | ||
| private int i; // Shared variable among the inner classes. | ||
|
|
||
| @BeforeEach | ||
| public void initTest() { i = 0; } | ||
|
|
||
| @Nested | ||
| public class AdditionTest { // COMPLIANT: Inner test class annotated with `@Nested`. | ||
| @Test | ||
| public void addTest1() { | ||
| assertEquals(1, i + 1); | ||
| } | ||
| } | ||
|
|
||
| public class SubtractionTest { // NON_COMPLIANT: Inner test class missing `@Nested`. | ||
| @Test | ||
| public void addTest1() { | ||
| assertEquals(-1, i - 1); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Implementation Notes | ||
|
|
||
| This rule is focused on missing `@Nested` annotations on non-static nested (inner) test classes. Static nested test classes should not be annotated with `@Nested`. As a result, the absence of a `@Nested` annotation on such classes is compliant. Identifying incorrect application of a `@Nested` annotation to static nested classes is out of scope for this rule. | ||
|
|
||
| ## References | ||
|
|
||
| - JUnit 5 API Documentation: [Annotation Interface Nested](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/Nested.html). | ||
| - JUnit 5 User Guide: [Nested Tests](https://junit.org/junit5/docs/current/user-guide/#writing-tests-nested). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| /** | ||
| * @id java/junit5-missing-nested-annotation | ||
| * @previous-id java/junit5-non-static-inner-class-missing-nested-annotation | ||
| * @name Missing `@Nested` annotation on JUnit 5 inner test class | ||
| * @description A JUnit 5 inner test class that is missing a `@Nested` annotation will be | ||
| * excluded from execution and may indicate a mistake from the | ||
| * programmer. | ||
| * @kind problem | ||
| * @precision very-high | ||
| * @problem.severity warning | ||
| * @tags quality | ||
| * reliability | ||
| * correctness | ||
| * testability | ||
| * frameworks/junit | ||
| */ | ||
|
|
||
| import java | ||
|
|
||
| from JUnit5InnerTestClass testClass | ||
| where not testClass.hasAnnotation("org.junit.jupiter.api", "Nested") | ||
| select testClass, "This JUnit 5 inner test class lacks a '@Nested' annotation." |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| import java.util.Collection; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.api.RepeatedTest; | ||
| import org.junit.jupiter.api.TestFactory; | ||
| import org.junit.jupiter.api.TestTemplate; | ||
| import org.junit.jupiter.api.Nested; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.ValueSource; | ||
|
|
||
| public class AnnotationTest { | ||
| @Nested | ||
| public class Test1 { // COMPLIANT: Inner test class has `@Nested` | ||
| @Test | ||
| public void test() { | ||
| } | ||
| } | ||
|
|
||
| // NON_COMPLIANT: Inner test class is missing `@Nested` | ||
| public class Test2_Test { // $ Alert | ||
| @Test | ||
| public void test() { | ||
| } | ||
| } | ||
|
|
||
| // NON_COMPLIANT: Inner test class is missing `@Nested` | ||
| public class Test2_RepeatedTest { // $ Alert | ||
| @RepeatedTest(2) | ||
| public void test() { | ||
| } | ||
| } | ||
|
|
||
| // NON_COMPLIANT: Inner test class is missing `@Nested` | ||
| public class Test2_ParameterizedTest { // $ Alert | ||
| @ParameterizedTest | ||
| @ValueSource(strings = { "" }) | ||
| public void test(String s) { | ||
| } | ||
| } | ||
|
|
||
| // NON_COMPLIANT: Inner test class is missing `@Nested` | ||
| public class Test2_TestFactory { // $ Alert | ||
| @TestFactory | ||
| Collection<Object> test() { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| // NON_COMPLIANT: Inner test class is missing `@Nested` | ||
| public class Test2_TestTemplate { // $ Alert | ||
| @TestTemplate | ||
| public void test() { | ||
| } | ||
| } | ||
|
|
||
| public class Test3 { // COMPLIANT: Since it is empty, it is not a test class | ||
| } | ||
|
|
||
| public class Test4 { // COMPLIANT: Since no methods have `@Test`, it is not a test class | ||
| public void f() { | ||
| } | ||
|
|
||
| public void g() { | ||
| } | ||
|
|
||
| public void h() { | ||
| } | ||
| } | ||
|
|
||
| public static class Test5 { // COMPLIANT: Static nested test classes don't need `@Nested` | ||
| @Test | ||
| public void test() { | ||
| } | ||
| } | ||
|
|
||
| // COMPLIANT: Invalid to use `@Nested` on a static class, but | ||
| // this matter is out of scope (see QHelp Implementation Notes) | ||
| @Nested | ||
| public static class Test6 { | ||
| @Test | ||
| public void test() { | ||
| } | ||
| } | ||
|
|
||
| public abstract class Test7 { // COMPLIANT: Abstract nested test classes don't need `@Nested` | ||
| @Test | ||
| public void test() { | ||
| } | ||
| } | ||
|
|
||
| interface Test8 { | ||
| } | ||
|
|
||
| public void f() { | ||
| // COMPLIANT: anonymous classes are not considered as inner test | ||
| // classes by JUnit and therefore don't need `@Nested` | ||
| new Test8() { | ||
| @Test | ||
| public void test() { | ||
| } | ||
| }; | ||
| // COMPLIANT: local classes are not considered as inner test | ||
| // classes by JUnit and therefore don't need `@Nested` | ||
| class Test9 { | ||
| @Test | ||
| void test() { | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // COMPLIANT: private classes are not considered as inner test | ||
| // classes by JUnit and therefore don't need `@Nested` | ||
| private class Test10 { | ||
| @Test | ||
| public void test() { | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| | AnnotationTest.java:19:16:19:25 | Test2_Test | This JUnit 5 inner test class lacks a '@Nested' annotation. | | ||
| | AnnotationTest.java:26:16:26:33 | Test2_RepeatedTest | This JUnit 5 inner test class lacks a '@Nested' annotation. | | ||
| | AnnotationTest.java:33:16:33:38 | Test2_ParameterizedTest | This JUnit 5 inner test class lacks a '@Nested' annotation. | | ||
| | AnnotationTest.java:41:16:41:32 | Test2_TestFactory | This JUnit 5 inner test class lacks a '@Nested' annotation. | | ||
| | AnnotationTest.java:49:16:49:33 | Test2_TestTemplate | This JUnit 5 inner test class lacks a '@Nested' annotation. | |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| query: Likely Bugs/Frameworks/JUnit/JUnit5MissingNestedAnnotation.ql | ||
| postprocess: utils/test/InlineExpectationsTestQuery.ql |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| //semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/junit-jupiter-api-5.2.0 |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.