0
# Test Annotations
1
2
Annotations are the primary mechanism in JUnit 4 for marking test methods and controlling test execution lifecycle. They provide a declarative way to define test structure, setup and teardown procedures, test execution rules, and method ordering.
3
4
## Capabilities
5
6
### Test Annotation
7
8
Marks a method as a test method. The test method will be executed by the JUnit runner. Supports optional `expected` parameter for exception testing and `timeout` parameter for performance testing.
9
10
```java { .api }
11
/**
12
* Marks a method as a test method
13
* @param expected - Optional: the exception class that the test is expected to throw
14
* @param timeout - Optional: timeout in milliseconds, test fails if it runs longer
15
*/
16
@Retention(RetentionPolicy.RUNTIME)
17
@Target(ElementType.METHOD)
18
public @interface Test {
19
Class<? extends Throwable> expected() default None.class;
20
long timeout() default 0L;
21
}
22
```
23
24
**Usage Examples:**
25
26
```java
27
import org.junit.Test;
28
import static org.junit.Assert.*;
29
30
public class BasicTest {
31
@Test
32
public void simpleTest() {
33
assertEquals(4, 2 + 2);
34
}
35
36
@Test(expected = IllegalArgumentException.class)
37
public void testException() {
38
throw new IllegalArgumentException("Expected exception");
39
}
40
41
@Test(timeout = 1000)
42
public void testTimeout() {
43
// Test fails if this takes more than 1 second
44
performQuickOperation();
45
}
46
}
47
```
48
49
### Before Annotation
50
51
Marks a method to run before each test method in the class. Use for test setup that needs to be fresh for each test. The method must be public void with no parameters.
52
53
```java { .api }
54
/**
55
* Marks a method to run before each test method
56
* The annotated method must be public, void, and take no parameters
57
*/
58
@Retention(RetentionPolicy.RUNTIME)
59
@Target(ElementType.METHOD)
60
public @interface Before {}
61
```
62
63
**Usage Examples:**
64
65
```java
66
import org.junit.Before;
67
import org.junit.Test;
68
import java.util.ArrayList;
69
import java.util.List;
70
71
public class ListTest {
72
private List<String> list;
73
74
@Before
75
public void setUp() {
76
list = new ArrayList<>();
77
list.add("initial");
78
}
79
80
@Test
81
public void testListSize() {
82
assertEquals(1, list.size());
83
}
84
85
@Test
86
public void testListAdd() {
87
list.add("second");
88
assertEquals(2, list.size());
89
}
90
}
91
```
92
93
### After Annotation
94
95
Marks a method to run after each test method in the class. Use for test cleanup and resource disposal. Runs even if the test or `@Before` method throws an exception.
96
97
```java { .api }
98
/**
99
* Marks a method to run after each test method
100
* The annotated method must be public, void, and take no parameters
101
* Runs even if the test or @Before method fails
102
*/
103
@Retention(RetentionPolicy.RUNTIME)
104
@Target(ElementType.METHOD)
105
public @interface After {}
106
```
107
108
**Usage Examples:**
109
110
```java
111
import org.junit.After;
112
import org.junit.Before;
113
import org.junit.Test;
114
import java.io.File;
115
import java.io.FileWriter;
116
import java.io.IOException;
117
118
public class FileTest {
119
private File tempFile;
120
121
@Before
122
public void createFile() throws IOException {
123
tempFile = File.createTempFile("test", ".txt");
124
}
125
126
@Test
127
public void testFileWrite() throws IOException {
128
FileWriter writer = new FileWriter(tempFile);
129
writer.write("test data");
130
writer.close();
131
assertTrue(tempFile.length() > 0);
132
}
133
134
@After
135
public void cleanup() {
136
if (tempFile != null && tempFile.exists()) {
137
tempFile.delete();
138
}
139
}
140
}
141
```
142
143
### BeforeClass Annotation
144
145
Marks a static method to run once before all test methods in the class. Use for expensive setup that can be shared across tests. The method must be public static void with no parameters.
146
147
```java { .api }
148
/**
149
* Marks a static method to run once before all test methods in the class
150
* The annotated method must be public, static, void, and take no parameters
151
*/
152
@Retention(RetentionPolicy.RUNTIME)
153
@Target(ElementType.METHOD)
154
public @interface BeforeClass {}
155
```
156
157
**Usage Examples:**
158
159
```java
160
import org.junit.BeforeClass;
161
import org.junit.Test;
162
import java.sql.Connection;
163
import java.sql.DriverManager;
164
165
public class DatabaseTest {
166
private static Connection connection;
167
168
@BeforeClass
169
public static void connectToDatabase() throws Exception {
170
// Expensive operation done once for all tests
171
connection = DriverManager.getConnection("jdbc:h2:mem:test");
172
connection.createStatement().execute("CREATE TABLE users (id INT, name VARCHAR(255))");
173
}
174
175
@Test
176
public void testInsert() throws Exception {
177
connection.createStatement().execute("INSERT INTO users VALUES (1, 'Alice')");
178
}
179
180
@Test
181
public void testQuery() throws Exception {
182
// Connection is already established
183
var result = connection.createStatement().executeQuery("SELECT COUNT(*) FROM users");
184
assertTrue(result.next());
185
}
186
}
187
```
188
189
### AfterClass Annotation
190
191
Marks a static method to run once after all test methods in the class. Use for cleanup of resources established in `@BeforeClass`. Runs even if tests fail.
192
193
```java { .api }
194
/**
195
* Marks a static method to run once after all test methods in the class
196
* The annotated method must be public, static, void, and take no parameters
197
* Runs even if tests or @BeforeClass method fail
198
*/
199
@Retention(RetentionPolicy.RUNTIME)
200
@Target(ElementType.METHOD)
201
public @interface AfterClass {}
202
```
203
204
**Usage Examples:**
205
206
```java
207
import org.junit.AfterClass;
208
import org.junit.BeforeClass;
209
import org.junit.Test;
210
211
public class ResourceTest {
212
private static ExpensiveResource resource;
213
214
@BeforeClass
215
public static void setUp() {
216
resource = new ExpensiveResource();
217
resource.initialize();
218
}
219
220
@Test
221
public void testOperation1() {
222
resource.performOperation();
223
}
224
225
@Test
226
public void testOperation2() {
227
resource.performAnotherOperation();
228
}
229
230
@AfterClass
231
public static void tearDown() {
232
if (resource != null) {
233
resource.cleanup();
234
resource = null;
235
}
236
}
237
}
238
```
239
240
### Ignore Annotation
241
242
Marks a test method or test class to be ignored (not executed). Useful for temporarily disabling tests without deleting them. Can include an optional message explaining why the test is ignored.
243
244
```java { .api }
245
/**
246
* Marks a test method or test class to be ignored
247
* @param value - Optional message explaining why the test is ignored
248
*/
249
@Retention(RetentionPolicy.RUNTIME)
250
@Target({ElementType.METHOD, ElementType.TYPE})
251
public @interface Ignore {
252
String value() default "";
253
}
254
```
255
256
**Usage Examples:**
257
258
```java
259
import org.junit.Ignore;
260
import org.junit.Test;
261
262
public class FeatureTest {
263
@Test
264
public void testWorkingFeature() {
265
// This test runs normally
266
assertTrue(true);
267
}
268
269
@Test
270
@Ignore("Feature not yet implemented")
271
public void testNewFeature() {
272
// This test is skipped
273
implementNewFeature();
274
}
275
276
@Test
277
@Ignore
278
public void testBrokenFeature() {
279
// This test is skipped without a message
280
brokenFunction();
281
}
282
}
283
284
// Ignore entire test class
285
@Ignore("Deprecated functionality, will be removed")
286
public class LegacyTest {
287
@Test
288
public void test1() {
289
// All tests in this class are ignored
290
}
291
292
@Test
293
public void test2() {
294
// This is also ignored
295
}
296
}
297
```
298
299
### Rule Annotation
300
301
Marks a field or method that returns a `TestRule` to be applied to each test method. Rules add behavior around test execution such as timeout enforcement, exception handling, or resource management.
302
303
```java { .api }
304
/**
305
* Marks a field or method that returns a TestRule
306
* The field must be public and of type TestRule or MethodRule
307
* If a method, it must be public and return TestRule or MethodRule
308
* @param order - Optional order for rule execution (default -1)
309
*/
310
@Retention(RetentionPolicy.RUNTIME)
311
@Target({ElementType.FIELD, ElementType.METHOD})
312
public @interface Rule {
313
int order() default -1;
314
}
315
```
316
317
**Usage Examples:**
318
319
```java
320
import org.junit.Rule;
321
import org.junit.Test;
322
import org.junit.rules.TemporaryFolder;
323
import org.junit.rules.Timeout;
324
import java.io.File;
325
import java.io.IOException;
326
327
public class RuleTest {
328
@Rule
329
public TemporaryFolder folder = new TemporaryFolder();
330
331
@Rule
332
public Timeout globalTimeout = Timeout.seconds(10);
333
334
@Test
335
public void testUsingTempFolder() throws IOException {
336
File file = folder.newFile("test.txt");
337
assertTrue(file.exists());
338
// File automatically deleted after test
339
}
340
341
@Test
342
public void testWithTimeout() {
343
// This test must complete within 10 seconds
344
performOperation();
345
}
346
}
347
```
348
349
### ClassRule Annotation
350
351
Marks a static field or method that returns a `TestRule` to be applied once for the entire test class. Useful for expensive setup that should be shared across all tests.
352
353
```java { .api }
354
/**
355
* Marks a static field or method that returns a TestRule for the entire class
356
* The field must be public static and of type TestRule
357
* If a method, it must be public static and return TestRule
358
* @param order - Optional order for rule execution (default -1)
359
*/
360
@Retention(RetentionPolicy.RUNTIME)
361
@Target({ElementType.FIELD, ElementType.METHOD})
362
public @interface ClassRule {
363
int order() default -1;
364
}
365
```
366
367
**Usage Examples:**
368
369
```java
370
import org.junit.ClassRule;
371
import org.junit.Test;
372
import org.junit.rules.ExternalResource;
373
import org.junit.rules.TemporaryFolder;
374
375
public class ClassRuleTest {
376
@ClassRule
377
public static TemporaryFolder folder = new TemporaryFolder();
378
379
@ClassRule
380
public static ExternalResource resource = new ExternalResource() {
381
@Override
382
protected void before() throws Throwable {
383
// Setup once for all tests
384
System.out.println("Starting test class");
385
}
386
387
@Override
388
protected void after() {
389
// Cleanup once after all tests
390
System.out.println("Finished test class");
391
}
392
};
393
394
@Test
395
public void test1() {
396
// Both rules are applied
397
}
398
399
@Test
400
public void test2() {
401
// Same rule instances used
402
}
403
}
404
```
405
406
### FixMethodOrder Annotation
407
408
Specifies the order in which test methods in a class should be executed. By default, JUnit executes methods in an unpredictable order for test isolation.
409
410
```java { .api }
411
/**
412
* Specifies the execution order for test methods in a class
413
* @param value - The ordering strategy to use
414
*/
415
@Retention(RetentionPolicy.RUNTIME)
416
@Target(ElementType.TYPE)
417
public @interface FixMethodOrder {
418
MethodSorters value();
419
}
420
```
421
422
**Usage Examples:**
423
424
```java
425
import org.junit.FixMethodOrder;
426
import org.junit.Test;
427
import org.junit.runners.MethodSorters;
428
429
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
430
public class OrderedTest {
431
@Test
432
public void test1_first() {
433
System.out.println("Runs first (alphabetically)");
434
}
435
436
@Test
437
public void test2_second() {
438
System.out.println("Runs second");
439
}
440
441
@Test
442
public void test3_third() {
443
System.out.println("Runs third");
444
}
445
}
446
447
@FixMethodOrder(MethodSorters.JVM)
448
public class JvmOrderTest {
449
// Tests run in JVM-returned order (varies by JVM)
450
@Test
451
public void testA() {}
452
453
@Test
454
public void testB() {}
455
}
456
```
457
458
### RunWith Annotation
459
460
Specifies a custom runner class to execute the tests instead of the default JUnit 4 runner. Essential for using alternative runners like `Suite`, `Parameterized`, or `Theories`.
461
462
```java { .api }
463
/**
464
* Specifies a custom runner class for executing tests
465
* @param value - The Runner class to use
466
*/
467
@Retention(RetentionPolicy.RUNTIME)
468
@Target(ElementType.TYPE)
469
@Inherited
470
public @interface RunWith {
471
Class<? extends Runner> value();
472
}
473
```
474
475
**Usage Examples:**
476
477
```java
478
import org.junit.runner.RunWith;
479
import org.junit.runners.Suite;
480
import org.junit.runners.Suite.SuiteClasses;
481
import org.junit.runners.Parameterized;
482
import org.junit.Test;
483
484
// Using Suite runner
485
@RunWith(Suite.class)
486
@SuiteClasses({TestClass1.class, TestClass2.class, TestClass3.class})
487
public class AllTests {
488
// Runs all tests from the specified classes
489
}
490
491
// Using Parameterized runner
492
@RunWith(Parameterized.class)
493
public class FibonacciTest {
494
@Parameterized.Parameter(0)
495
public int input;
496
497
@Parameterized.Parameter(1)
498
public int expected;
499
500
@Parameterized.Parameters
501
public static Collection<Object[]> data() {
502
return Arrays.asList(new Object[][] {
503
{0, 0}, {1, 1}, {2, 1}, {3, 2}, {4, 3}, {5, 5}
504
});
505
}
506
507
@Test
508
public void testFibonacci() {
509
assertEquals(expected, Fibonacci.compute(input));
510
}
511
}
512
```
513
514
### ValidateWith Annotation
515
516
Allows for a custom validator to be attached to an annotation. When attached to an annotation, the validator will be instantiated and invoked by the test runner.
517
518
```java { .api }
519
/**
520
* Attaches an AnnotationValidator to an annotation
521
* @param value - The AnnotationValidator class to use
522
* @since 4.12
523
*/
524
@Retention(RetentionPolicy.RUNTIME)
525
@Target(ElementType.ANNOTATION_TYPE)
526
@Inherited
527
public @interface ValidateWith {
528
Class<? extends AnnotationValidator> value();
529
}
530
531
/**
532
* Validates annotations on classes, methods, and fields.
533
* Instances must be immutable and thread-safe as they are shared by multiple test runners.
534
* @since 4.12
535
*/
536
public abstract class AnnotationValidator {
537
/**
538
* Validates annotation on the given class
539
* @param testClass - Class being validated
540
* @return List of validation exceptions (empty if valid)
541
*/
542
public List<Exception> validateAnnotatedClass(TestClass testClass);
543
544
/**
545
* Validates annotation on the given field
546
* @param field - Field being validated
547
* @return List of validation exceptions (empty if valid)
548
*/
549
public List<Exception> validateAnnotatedField(FrameworkField field);
550
551
/**
552
* Validates annotation on the given method
553
* @param method - Method being validated
554
* @return List of validation exceptions (empty if valid)
555
*/
556
public List<Exception> validateAnnotatedMethod(FrameworkMethod method);
557
}
558
```
559
560
**Usage Examples:**
561
562
```java
563
import org.junit.validator.ValidateWith;
564
import org.junit.validator.AnnotationValidator;
565
import org.junit.runners.model.TestClass;
566
567
import java.lang.annotation.*;
568
import java.util.ArrayList;
569
import java.util.List;
570
571
// Define custom annotation with validator
572
@Retention(RetentionPolicy.RUNTIME)
573
@Target(ElementType.TYPE)
574
@ValidateWith(RequiresJava8Validator.class)
575
public @interface RequiresJava8 {}
576
577
// Implement the validator
578
public class RequiresJava8Validator extends AnnotationValidator {
579
@Override
580
public List<Exception> validateAnnotatedClass(TestClass testClass) {
581
List<Exception> errors = new ArrayList<>();
582
String javaVersion = System.getProperty("java.version");
583
if (!javaVersion.startsWith("1.8") && !javaVersion.startsWith("8")) {
584
errors.add(new Exception(
585
"Test requires Java 8 but running on " + javaVersion));
586
}
587
return errors;
588
}
589
}
590
591
// Use the validated annotation
592
@RequiresJava8
593
public class Java8OnlyTest {
594
@Test
595
public void testStreamApi() {
596
// Test will only run on Java 8+
597
}
598
}
599
```
600
601
### OrderWith Annotation
602
603
Specifies a custom ordering for test methods. Allows fine-grained control over test execution order beyond the basic alphabetical or JVM ordering.
604
605
```java { .api }
606
/**
607
* Specifies an ordering for test methods
608
* @param value - The Ordering.Factory class to use
609
*/
610
@Retention(RetentionPolicy.RUNTIME)
611
@Target(ElementType.TYPE)
612
@Inherited
613
@ValidateWith(OrderWithValidator.class)
614
public @interface OrderWith {
615
Class<? extends Ordering.Factory> value();
616
}
617
```
618
619
**Usage Examples:**
620
621
```java
622
import org.junit.runner.OrderWith;
623
import org.junit.runner.manipulation.Alphanumeric;
624
import org.junit.Test;
625
626
@OrderWith(Alphanumeric.class)
627
public class CustomOrderedTest {
628
@Test
629
public void testA() {
630
// Tests run in alphanumeric order
631
System.out.println("Test A");
632
}
633
634
@Test
635
public void testB() {
636
System.out.println("Test B");
637
}
638
}
639
```
640
641
## Types
642
643
```java { .api }
644
/**
645
* Method ordering strategies for @FixMethodOrder
646
*/
647
public enum MethodSorters {
648
/**
649
* Sorts methods by method name, in lexicographic order
650
*/
651
NAME_ASCENDING,
652
653
/**
654
* Leaves test methods in order returned by JVM (may vary)
655
*/
656
JVM,
657
658
/**
659
* Default - pseudo-random order that changes between runs
660
*/
661
DEFAULT
662
}
663
664
/**
665
* Placeholder class for @Test annotation's default expected value
666
*/
667
public class None extends Throwable {
668
private None() {}
669
}
670
```
671