Skip to content

Commit 7228992

Browse files
authored
TEST-24 Added support for annotation-based JUnit method rules (#25)
added support for annotation-based JUnit method rules added support for parameterized runners to annotation processor
1 parent ddc2162 commit 7228992

19 files changed

+1020
-25
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ Repository of re-usable test utilities.
33

44
### Modules
55
The following modules are defined:
6+
* internal
67
* spock-all
78
* junit-extensions
89
* spock-extensions
910
* hamcrest-extensions
1011
* mockito-extensions
1112
* failsafe-controller
1213

14+
#### internal
15+
Defines a set of common utilities used internally by all other modules. This module is not meant to be depended on from outside this workspace.
16+
1317
#### spock-all
1418
Defines a single pom artifact that contains all dependencies required to develop and execute Spock specifications. Simply add the following to your pom file to be able to compile and run Spock specifications:
1519

docs/failsafe-controller.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#Testing Failsafe Code
1+
## Testing Failsafe Code
22

33
Failsafe (from `net.jodah.failsafe`) is a really nice library that is typically used in code to repeatedly execute a given task until it succeeds or aborts. Although it can be used synchronously, it is often used asynchronously using an executor such that the tasks are executed by other threads.
44

docs/junit-extensions.md

+178-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,36 @@
1-
#JUnit Extensions
1+
## JUnit Extensions
2+
3+
JUnit extensions are divided in new method rules, method rule annotations, and test runners.
4+
5+
### JUnit Method Rules
6+
7+
#### MethodRuleAnnotationProcessor
8+
The [MethodRuleAnnotationProcessor](../junit-extensions/src/main/java/org/codice/junit/rules/MethodRuleAnnotationProcessor.java) provides a JUnit4 method rule that implements a similar support to Spock extensions by allowing specification of simple JUnit method rules via the meta-annotation [ExtensionMethodRuleAnnotation](../junit-extensions/src/main/java/org/codice/junit/ExtensionMethodRuleAnnotation.java).
9+
10+
New annotations created referencing a JUnit method rule can be used to annotate JUnit test class when the method rule should apply to all tests in that class. These anntations can also be used to annotate a particular test method if the method rule should only apply to that test method.
11+
12+
The order these new annotations are defined in will dictate the order they will be applied from outermost to innermost.
13+
14+
Method rules referenced in this manner will not retain any states from one test method to the next as they will be re-instantiated each time. The method rule class must define either a public constructor with a single argument of the same annotation type as the annotation that is annotated with the meta-annotation [ExtensionMethodRuleAnnotation](../junit-extensions/src/main/java/org/codice/junit/ExtensionMethodRuleAnnotation.java) or a public default constructor. Defining a constructor with the annotation as a parameter allows customization of the method rule using your own annotation.
15+
16+
17+
```
18+
@RestoreSystemProperties // will apply to each test methods
19+
public class MyTest {
20+
@Rule public final MethodRuleAnnotationProcessor processor = new MethodRuleAnnotationProcessor();
21+
22+
@ClearInterruptions // will only apply to this test method
23+
@Test
24+
public void testSomething() throws Exception {
25+
}
26+
27+
@Test
28+
public void testSomethingElse() throws Exception {
29+
}
30+
}
31+
```
32+
33+
The advantage of using the test runner [MethodRuleAnnotationRunner](../junit-extensions/src/main/java/org/codice/junit/MethodRuleAnnotationRunner.java) over the method rule [MethodRuleAnnotationProcessor](../junit-extensions/src/main/java/org/codice/junit/rules/MethodRuleAnnotationProcessor.java) is that it guarantees that all annotation-based rules will be considered outermost compared to any rules defined within the test class whereas the processor cannot guarantee that since it is at the mercy of JUnit in terms of how the rules are internally initialized.
234

335
#### RestoreSystemProperties
436
The [RestoreSystemProperties](../junit-extensions/src/main/java/org/codice/junit/rules/RestoreSystemProperties.java) provides a Java version of the Spock annotation which when defined as a JUnit rule will automatically reset the system properties to their initial values after each tests.
@@ -17,7 +49,7 @@ The [RestoreSystemProperties](../junit-extensions/src/main/java/org/codice/junit
1749
```
1850

1951
#### ClearInterruptions
20-
The [ClearInterruptions](../junit-extensions/src/main/java/org/codice/junit/rules/ClearInterruptions.java) provides a Java version of the Spock annotation which when defined as a JUnit rule will clear interruption states from the current thread after testing. For example:
52+
The [ClearInterruptions](../junit-extensions/src/main/java/org/codice/junit/rules/ClearInterruptions.java) provides a Java version of the Spock annotation which when defined as a JUnit rule will clear interruption state from the current thread after testing. For example:
2153
```
2254
public class MyTest {
2355
@Rule public final ClearInterruptions clearInterruptions = new ClearInterruptions();
@@ -34,6 +66,150 @@ The [ClearInterruptions](../junit-extensions/src/main/java/org/codice/junit/rule
3466
}
3567
```
3668

69+
#### MethodRuleChain
70+
The [MethodRuleChain](../junit-extensions/src/main/java/org/codice/junit/rules/MethodRuleChain.java) provides a JUnit4 rule that can be used to create a controlled chain of method rules when the order they are processed is important.
71+
72+
```
73+
public class MyTest {
74+
@Rule public final MethodRuleChain chain =
75+
MethodRuleChain
76+
.outer(new RestoreSystemProperties())
77+
.around(new ClearInterruptions());
78+
79+
@Test
80+
public void testSomething() throws Exception {
81+
System.setProperty("ddf.home", "some new location");
82+
final MyClass obj = new MyClass(some);
83+
84+
obj.doSomething();
85+
}
86+
}
87+
```
88+
89+
### JUnit Method Rule Annotations
90+
91+
#### RestoreSystemProperties
92+
The [RestoreSystemProperties](../junit-extensions/src/main/java/org/codice/junit/RestoreSystemProperties.java) annotation can be used in conjunction with the MethodRuleAnnotationProcessor JUnit method rule to indicate all methods of a test class or specific ones where system properties should automatically be reset to their initial values after testing.
93+
94+
#### ClearInterruptions
95+
The [ClearInterruptions](../junit-extensions/src/main/java/org/codice/junit/ClearInterruptions.java) annotation can be used in conjunction with the MethodRuleAnnotationProcessor JUnit method rule to indicate all methods of a test class or specific ones where the interruption state from the current thread should automatically be cleared after testing.
96+
97+
### JUnit Test Runners
98+
99+
#### MethodRuleAnnotationRunner
100+
The [MethodRuleAnnotationRunner](../junit-extensions/src/main/java/org/codice/junit/MethodRuleAnnotationRunner.java) provides a JUnit4 test runner that supports annotation-based method rules similar support to Spock extensions by allowing specification of simple JUnit method rules via the meta-annotation [ExtensionMethodRuleAnnotation](../junit-extensions/src/main/java/org/codice/junit/ExtensionMethodRuleAnnotation.java).
101+
102+
New annotations created referencing another JUnit method rule can be used to annotate JUnit test class when the method rule should apply to all tests in that class. These annotations can also be used to annotate a particular test method if the method rule should only apply to that test method.
103+
104+
The order these new annotations are defined in will dictate the order they will be applied from outermost to innermost.
105+
106+
Method rules referenced in this manner will not retain any states from one test method to the next as they will be re-instantiated each time. The method rule class must define either a public constructor with a single argument of the same annotation type as the annotation that is annotated with the meta-annotation [ExtensionMethodRuleAnnotation](../junit-extensions/src/main/java/org/codice/junit/ExtensionMethodRuleAnnotation.java) or a public default constructor. Defining a constructor with the annotation as a parameter allows customization of the method rule using your own annotation.
107+
108+
```
109+
@RestoreSystemProperties // will apply to each test methods
110+
@RunWith(MethodRuleAnnotationRunner.class)
111+
public class MyTest {
112+
@ClearInterruptions // will only apply to this test method
113+
@Test
114+
public void testSomething() throws Exception {
115+
}
116+
117+
@Test
118+
public void testSomethingElse() throws Exception {
119+
}
120+
}
121+
```
122+
123+
Example of a method rule annotation that can be customized:
124+
125+
```
126+
public MyMethodRule implements MethodRule {
127+
private final long timeout;
128+
129+
public MyMethodRule(MyAnnotation annotation) {
130+
this.timeout = annotation.timeout();
131+
}
132+
133+
@Override
134+
public Statement apply(Statement statement, FrameworkMethod frameworkMethod, Object o) {
135+
...
136+
}
137+
}
138+
139+
@ExtensionMethodRuleAnnotation(MyMethodRule.class)
140+
@Target({ElementType.TYPE, ElementType.METHOD})
141+
@Retention(RetentionPolicy.RUNTIME)
142+
@Inherited
143+
@Documented
144+
public @interface MyAnnotation {
145+
long timeout();
146+
}
147+
```
148+
149+
The advantage of using the test runner [MethodRuleAnnotationRunner](../junit-extensions/src/main/java/org/codice/junit/MethodRuleAnnotationRunner.java) over the method rule [MethodRuleAnnotationProcessor](../junit-extensions/src/main/java/org/codice/junit/rules/MethodRuleAnnotationProcessor.java) is that it guarantees that all annotation-based rules will be considered outermost compared to any rules defined within the test class whereas the processor cannot guarantee that since it is at the mercy of JUnit in terms of how the rules are internally initialized.
150+
151+
#### MethodRuleAnnotationRunnerWithParametersFactory
152+
The [MethodRuleAnnotationRunnerWithParametersFactory](../junit-extensions/src/main/java/org/codice/junit/parameterized/MethodRuleAnnotationRunnerWithParametersFactory.java) provides a JUnit4 parameter factory to create test runners that allows specification of method rules via the meta-annotation [ExtensionMethodRuleAnnotation](../junit-extensions/src/main/java/org/codice/junit/ExtensionMethodRuleAnnotation.java).
153+
Such a factory can be specified when using the `Parameterized` test runner to run parameterized tests.
154+
155+
New annotations created referencing another JUnit method rule can be used to annotate JUnit test class when the method rule should apply to all tests in that class. These annotations can also be used to annotate a particular test method if the method rule should only apply to that test method.
156+
157+
The order these new annotations are defined in will dictate the order they will be applied from outermost to innermost.
158+
159+
Method rules referenced in this manner will not retain any states from one test method to the next as they will be re-instantiated each time. The method rule class must define either a public constructor with a single argument of the same annotation type as the annotation that is annotated with the meta-annotation [ExtensionMethodRuleAnnotation](../junit-extensions/src/main/java/org/codice/junit/ExtensionMethodRuleAnnotation.java) or a public default constructor. Defining a constructor with the annotation as a parameter allows customization of the method rule using your own annotation.
160+
161+
```
162+
@RestoreSystemProperties // will apply to each test methods
163+
@RunWith(Parameterized)
164+
@UseParametersRunnersFactory(MethodRuleAnnotationRunnerWithParametersFactory.class)
165+
public class MyTest {
166+
@Parameters
167+
public static Object[] data() {
168+
return new Object[] { "first test", "second test" };
169+
}
170+
171+
@Parameter
172+
public String input;
173+
174+
@ClearInterruptions // will only apply to this test method
175+
@Test
176+
public void testSomething() throws Exception {
177+
}
178+
179+
@Test
180+
public void testSomethingElse() throws Exception {
181+
}
182+
}
183+
```
184+
185+
Example of a method rule annotation that can be customized:
186+
187+
```
188+
public MyMethodRule implements MethodRule {
189+
private final long timeout;
190+
191+
public MyMethodRule(MyAnnotation annotation) {
192+
this.timeout = annotation.timeout();
193+
}
194+
195+
@Override
196+
public Statement apply(Statement statement, FrameworkMethod frameworkMethod, Object o) {
197+
...
198+
}
199+
}
200+
201+
@ExtensionMethodRuleAnnotation(MyMethodRule.class)
202+
@Target({ElementType.TYPE, ElementType.METHOD})
203+
@Retention(RetentionPolicy.RUNTIME)
204+
@Inherited
205+
@Documented
206+
public @interface MyAnnotation {
207+
long timeout();
208+
}
209+
```
210+
211+
The advantage of using the test runner [MethodRuleAnnotationRunner](../junit-extensions/src/main/java/org/codice/junit/MethodRuleAnnotationRunner.java) over the method rule [MethodRuleAnnotationProcessor](../junit-extensions/src/main/java/org/codice/junit/rules/MethodRuleAnnotationProcessor.java) is that it guarantees that all annotation-based rules will be considered outermost compared to any rules defined within the test class whereas the processor cannot guarantee that since it is at the mercy of JUnit in terms of how the rules are internally initialized.
212+
37213
#### Definalizer
38214
The [Definalizer JUnit test runner](../junit-extensions/src/main/java/org/codice/junit/DeFinalizer.java) is designed as a generic proxy test runner for another JUnit test runner by indirectly instantiating that runner in order to add support for de-finalizing (i.e. removing the final constraint) 3rd party Java classes that need to be mocked or stubbed during testing.
39215
It does so by creating a classloader designed with an aggressive strategy where it will load all classes first before delegating to its parent. This classloader will therefore reload all classes while definalizing those that are requested except for all classes in the following packages:

docs/spock-extensions.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#Spock Extensions
1+
## Spock Extensions
22

33
#### @ClearInterruptions
44
The [`@ClearInterruptions`](../spock-extensions/src/main/groovy/org/codice/spock/ClearInterruptions.groovy) annotation can be used to clear interruption states from the current thread after testing. For example:

internal/pom.xml

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright (c) Codice Foundation
5+
*
6+
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either
7+
* version 3 of the License, or any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10+
* See the GNU Lesser General Public License for more details. A copy of the GNU Lesser General Public License is distributed along with this program and can be found at
11+
* <http://www.gnu.org/licenses/lgpl.html>.
12+
*
13+
**/
14+
-->
15+
<project xmlns="http://maven.apache.org/POM/4.0.0"
16+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
17+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
18+
<modelVersion>4.0.0</modelVersion>
19+
20+
<parent>
21+
<groupId>org.codice.test</groupId>
22+
<artifactId>codice-test</artifactId>
23+
<version>0.4-SNAPSHOT</version>
24+
</parent>
25+
26+
<name>Codice Test :: Internal</name>
27+
<artifactId>internal</artifactId>
28+
<packaging>jar</packaging>
29+
30+
<dependencies>
31+
<dependency>
32+
<groupId>org.apache.servicemix.bundles</groupId>
33+
<artifactId>org.apache.servicemix.bundles.jsr305</artifactId>
34+
</dependency>
35+
<dependency>
36+
<groupId>commons-lang</groupId>
37+
<artifactId>commons-lang</artifactId>
38+
</dependency>
39+
40+
<dependency>
41+
<groupId>junit</groupId>
42+
<artifactId>junit</artifactId>
43+
</dependency>
44+
45+
<dependency>
46+
<groupId>org.codice.test</groupId>
47+
<artifactId>spock-all</artifactId>
48+
<version>${project.version}</version>
49+
<type>pom</type>
50+
<scope>test</scope>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.codehaus.groovy</groupId>
54+
<artifactId>groovy-all</artifactId>
55+
<scope>test</scope>
56+
</dependency>
57+
</dependencies>
58+
59+
<build>
60+
<plugins>
61+
<plugin>
62+
<groupId>org.jacoco</groupId>
63+
<artifactId>jacoco-maven-plugin</artifactId>
64+
<executions>
65+
<execution>
66+
<id>default-check</id>
67+
<goals>
68+
<goal>check</goal>
69+
</goals>
70+
<configuration>
71+
<haltOnFailure>true</haltOnFailure>
72+
<rules>
73+
<rule>
74+
<element>BUNDLE</element>
75+
<limits>
76+
<limit implementation="org.codice.jacoco.LenientLimit">
77+
<counter>INSTRUCTION</counter>
78+
<value>COVEREDRATIO</value>
79+
<minimum>0.57</minimum>
80+
</limit>
81+
<limit implementation="org.codice.jacoco.LenientLimit">
82+
<counter>BRANCH</counter>
83+
<value>COVEREDRATIO</value>
84+
<minimum>0.63</minimum>
85+
</limit>
86+
<limit implementation="org.codice.jacoco.LenientLimit">
87+
<counter>COMPLEXITY</counter>
88+
<value>COVEREDRATIO</value>
89+
<minimum>0.41</minimum>
90+
</limit>
91+
</limits>
92+
</rule>
93+
</rules>
94+
</configuration>
95+
</execution>
96+
</executions>
97+
</plugin>
98+
</plugins>
99+
</build>
100+
</project>

0 commit comments

Comments
 (0)