0
# Categories
1
2
Categories is an experimental feature for grouping and filtering tests using marker interfaces. It allows selective test execution based on category annotations, useful for organizing tests by type (unit, integration, performance) or other criteria.
3
4
## Capabilities
5
6
### Category Annotation
7
8
Marks tests or test classes as belonging to one or more categories. Categories are defined as marker interfaces.
9
10
```java { .api }
11
/**
12
* Marks a test method or class as belonging to categories
13
* @param value - Category interface(s) this test belongs to
14
*/
15
@Retention(RetentionPolicy.RUNTIME)
16
@Inherited
17
@ValidateWith(CategoryValidator.class)
18
public @interface Category {
19
Class<?>[] value();
20
}
21
```
22
23
**Usage Examples:**
24
25
```java
26
import org.junit.Test;
27
import org.junit.experimental.categories.Category;
28
import static org.junit.Assert.*;
29
30
// Define category marker interfaces
31
public interface SlowTests {}
32
public interface FastTests {}
33
public interface IntegrationTests {}
34
public interface UnitTests {}
35
36
// Categorize entire test class
37
@Category(UnitTests.class)
38
public class CalculatorTest {
39
@Test
40
public void testAddition() {
41
assertEquals(5, 2 + 3);
42
}
43
44
@Test
45
@Category(SlowTests.class)
46
public void testComplexCalculation() {
47
// This test is both UnitTests and SlowTests
48
performExpensiveCalculation();
49
}
50
}
51
52
// Multiple categories
53
@Category({IntegrationTests.class, SlowTests.class})
54
public class DatabaseTest {
55
@Test
56
public void testConnection() {
57
// Integration and slow test
58
connectToDatabase();
59
}
60
61
@Test
62
@Category(FastTests.class)
63
public void testConfiguration() {
64
// Override: this is fast despite class being slow
65
checkConfig();
66
}
67
}
68
69
// Method-level categories
70
public class MixedTest {
71
@Test
72
@Category(FastTests.class)
73
public void fastTest() {
74
assertTrue(true);
75
}
76
77
@Test
78
@Category(SlowTests.class)
79
public void slowTest() {
80
Thread.sleep(1000);
81
}
82
83
@Test
84
@Category({IntegrationTests.class, SlowTests.class})
85
public void integrationTest() {
86
testExternalSystem();
87
}
88
}
89
```
90
91
### Categories Runner
92
93
Special suite runner that filters tests based on included and excluded categories.
94
95
```java { .api }
96
/**
97
* Suite runner that filters tests by category
98
* Only runs tests matching category criteria
99
*/
100
@RunWith(Suite.class)
101
public class Categories extends Suite {
102
/**
103
* Creates Categories runner
104
* @param klass - Suite class
105
* @param builder - Runner builder
106
* @throws InitializationError if initialization fails
107
*/
108
public Categories(Class<?> klass, RunnerBuilder builder) throws InitializationError;
109
110
/**
111
* Specify categories to include
112
* Only tests with these categories will run
113
*/
114
@Retention(RetentionPolicy.RUNTIME)
115
@Target(ElementType.TYPE)
116
public @interface IncludeCategory {
117
/**
118
* Categories to include
119
*/
120
Class<?>[] value() default {};
121
122
/**
123
* If true, runs tests annotated with any of the categories
124
* Otherwise, runs tests only if annotated with all categories
125
*/
126
boolean matchAny() default true;
127
}
128
129
/**
130
* Specify categories to exclude
131
* Tests with these categories will not run
132
*/
133
@Retention(RetentionPolicy.RUNTIME)
134
@Target(ElementType.TYPE)
135
public @interface ExcludeCategory {
136
/**
137
* Categories to exclude
138
*/
139
Class<?>[] value() default {};
140
141
/**
142
* If true, excludes tests annotated with any of the categories
143
* Otherwise, excludes tests only if annotated with all categories
144
*/
145
boolean matchAny() default true;
146
}
147
}
148
```
149
150
**Usage Examples:**
151
152
```java
153
import org.junit.experimental.categories.Categories;
154
import org.junit.experimental.categories.Categories.IncludeCategory;
155
import org.junit.experimental.categories.Categories.ExcludeCategory;
156
import org.junit.runner.RunWith;
157
import org.junit.runners.Suite.SuiteClasses;
158
159
// Run only fast tests
160
@RunWith(Categories.class)
161
@IncludeCategory(FastTests.class)
162
@SuiteClasses({
163
CalculatorTest.class,
164
StringUtilsTest.class,
165
DateUtilsTest.class,
166
DatabaseTest.class
167
})
168
public class FastTestSuite {
169
// Only tests marked with @Category(FastTests.class) will run
170
}
171
172
// Run all except slow tests
173
@RunWith(Categories.class)
174
@ExcludeCategory(SlowTests.class)
175
@SuiteClasses({
176
CalculatorTest.class,
177
DatabaseTest.class,
178
NetworkTest.class
179
})
180
public class QuickTestSuite {
181
// All tests except those marked @Category(SlowTests.class)
182
}
183
184
// Run integration tests only
185
@RunWith(Categories.class)
186
@IncludeCategory(IntegrationTests.class)
187
@SuiteClasses({
188
DatabaseTest.class,
189
ApiTest.class,
190
NetworkTest.class,
191
FileSystemTest.class
192
})
193
public class IntegrationTestSuite {
194
}
195
196
// Include and exclude together
197
@RunWith(Categories.class)
198
@IncludeCategory(IntegrationTests.class)
199
@ExcludeCategory(SlowTests.class)
200
@SuiteClasses({
201
DatabaseTest.class,
202
ApiTest.class,
203
NetworkTest.class
204
})
205
public class FastIntegrationSuite {
206
// Only fast integration tests
207
}
208
```
209
210
### CategoryFilter
211
212
Programmatic filtering of tests by category. Used for custom test execution.
213
214
```java { .api }
215
/**
216
* Filter for selecting tests by category
217
*/
218
public class CategoryFilter extends Filter {
219
/**
220
* Create filter that includes only matching categories
221
* @param categoryType - Category to include
222
* @return CategoryFilter
223
*/
224
public static CategoryFilter include(Class<?> categoryType);
225
226
/**
227
* Create filter that includes only matching categories
228
* @param includes - Categories to include
229
* @return CategoryFilter
230
*/
231
public static CategoryFilter include(Class<?>... includes);
232
233
/**
234
* Create filter that excludes matching categories
235
* @param categoryType - Category to exclude
236
* @return CategoryFilter
237
*/
238
public static CategoryFilter exclude(Class<?> categoryType);
239
240
/**
241
* Create filter that excludes matching categories
242
* @param excludes - Categories to exclude
243
* @return CategoryFilter
244
*/
245
public static CategoryFilter exclude(Class<?>... excludes);
246
247
/**
248
* Create filter with include and exclude rules
249
* @param matchAnyInclusions - Whether to match any (true) or all (false) inclusion categories
250
* @param inclusions - Set of categories to include
251
* @param matchAnyExclusions - Whether to match any (true) or all (false) exclusion categories
252
* @param exclusions - Set of categories to exclude
253
* @return CategoryFilter
254
*/
255
public static CategoryFilter categoryFilter(
256
boolean matchAnyInclusions,
257
Set<Class<?>> inclusions,
258
boolean matchAnyExclusions,
259
Set<Class<?>> exclusions
260
);
261
262
/**
263
* Check if test should run
264
* @param description - Test description
265
* @return true if test should run
266
*/
267
public boolean shouldRun(Description description);
268
269
/**
270
* Get filter description
271
* @return Description string
272
*/
273
public String describe();
274
}
275
```
276
277
**Usage Examples:**
278
279
```java
280
import org.junit.experimental.categories.CategoryFilter;
281
import org.junit.runner.JUnitCore;
282
import org.junit.runner.Request;
283
import org.junit.runner.Result;
284
import java.util.Set;
285
import java.util.HashSet;
286
287
public class CategoryRunner {
288
public static void main(String[] args) {
289
// Run only fast tests
290
Request request = Request.aClass(AllTests.class);
291
request = request.filterWith(CategoryFilter.include(FastTests.class));
292
Result result = new JUnitCore().run(request);
293
294
// Run all except slow tests
295
Request request2 = Request.aClass(AllTests.class);
296
request2 = request2.filterWith(CategoryFilter.exclude(SlowTests.class));
297
Result result2 = new JUnitCore().run(request2);
298
299
// Complex filtering with categoryFilter
300
Set<Class<?>> inclusions = new HashSet<>();
301
inclusions.add(IntegrationTests.class);
302
Set<Class<?>> exclusions = new HashSet<>();
303
exclusions.add(SlowTests.class);
304
305
CategoryFilter filter = CategoryFilter.categoryFilter(
306
true, // Match any inclusion category
307
inclusions, // Include integration tests
308
true, // Match any exclusion category
309
exclusions // Exclude slow tests
310
);
311
Request request3 = Request.aClass(AllTests.class);
312
request3 = request3.filterWith(filter);
313
Result result3 = new JUnitCore().run(request3);
314
}
315
}
316
317
// Custom test runner with categories
318
public class CustomCategoryRunner {
319
public static Result runCategories(Class<?> testClass, Class<?>... categories) {
320
Request request = Request.aClass(testClass);
321
request = request.filterWith(CategoryFilter.include(categories));
322
return new JUnitCore().run(request);
323
}
324
325
public static void main(String[] args) {
326
// Run specific categories
327
Result result = runCategories(
328
MyTestSuite.class,
329
FastTests.class,
330
UnitTests.class
331
);
332
System.out.println("Tests run: " + result.getRunCount());
333
}
334
}
335
```
336
337
## Common Category Patterns
338
339
### Test Type Categories
340
341
Organize tests by their nature and scope.
342
343
```java
344
// Marker interfaces for test types
345
public interface UnitTests {}
346
public interface IntegrationTests {}
347
public interface EndToEndTests {}
348
public interface PerformanceTests {}
349
public interface SecurityTests {}
350
351
@Category(UnitTests.class)
352
public class BusinessLogicTest {
353
@Test
354
public void testCalculation() {
355
// Pure unit test
356
}
357
}
358
359
@Category(IntegrationTests.class)
360
public class DatabaseIntegrationTest {
361
@Test
362
public void testDatabaseQuery() {
363
// Tests with database
364
}
365
}
366
367
@Category(EndToEndTests.class)
368
public class UserFlowTest {
369
@Test
370
public void testCompleteUserJourney() {
371
// Full system test
372
}
373
}
374
```
375
376
### Speed Categories
377
378
Group tests by execution time for quick feedback loops.
379
380
```java
381
public interface FastTests {} // < 100ms
382
public interface MediumTests {} // 100ms - 1s
383
public interface SlowTests {} // > 1s
384
385
@Category(FastTests.class)
386
public class QuickUnitTest {
387
@Test
388
public void instantTest() {
389
assertEquals(4, 2 + 2);
390
}
391
}
392
393
@Category(SlowTests.class)
394
public class HeavyIntegrationTest {
395
@Test
396
public void testLargeDataset() {
397
processMillionRecords();
398
}
399
}
400
```
401
402
### Environment Categories
403
404
Tests that require specific environments or resources.
405
406
```java
407
public interface RequiresDatabase {}
408
public interface RequiresNetwork {}
409
public interface RequiresDocker {}
410
public interface RequiresLinux {}
411
public interface RequiresWindows {}
412
413
@Category(RequiresDatabase.class)
414
public class DatabaseTest {
415
@Test
416
public void testQuery() {
417
// Needs database
418
}
419
}
420
421
@Category({RequiresNetwork.class, RequiresDocker.class})
422
public class MicroserviceTest {
423
@Test
424
public void testServiceCommunication() {
425
// Needs network and Docker
426
}
427
}
428
```
429
430
### Feature Categories
431
432
Organize by feature or module being tested.
433
434
```java
435
public interface PaymentTests {}
436
public interface AuthenticationTests {}
437
public interface ReportingTests {}
438
public interface NotificationTests {}
439
440
@Category(PaymentTests.class)
441
public class PaymentProcessorTest {
442
@Test
443
public void testPayment() {
444
processPayment();
445
}
446
}
447
448
@Category(AuthenticationTests.class)
449
public class LoginTest {
450
@Test
451
public void testLogin() {
452
authenticateUser();
453
}
454
}
455
```
456
457
### Build Pipeline Categories
458
459
Different category suites for different build stages.
460
461
```java
462
// CI pipeline categories
463
public interface CommitTests {} // Run on every commit
464
public interface NightlyTests {} // Run nightly
465
public interface WeeklyTests {} // Run weekly
466
public interface ManualTests {} // Run manually only
467
468
// Commit stage - fast tests only
469
@RunWith(Categories.class)
470
@IncludeCategory(CommitTests.class)
471
@SuiteClasses({/* all test classes */})
472
public class CommitStageSuite {}
473
474
// Nightly build - all automated tests
475
@RunWith(Categories.class)
476
@ExcludeCategory(ManualTests.class)
477
@SuiteClasses({/* all test classes */})
478
public class NightlyBuildSuite {}
479
480
// Usage in tests
481
@Category(CommitTests.class)
482
public class FastCoreTest {
483
// Runs on every commit
484
}
485
486
@Category({NightlyTests.class, SlowTests.class})
487
public class ExtensiveTest {
488
// Runs in nightly builds
489
}
490
```
491
492
## Maven Integration
493
494
Run specific categories from Maven command line.
495
496
```xml
497
<!-- pom.xml configuration -->
498
<build>
499
<plugins>
500
<plugin>
501
<groupId>org.apache.maven.plugins</groupId>
502
<artifactId>maven-surefire-plugin</artifactId>
503
<version>2.22.2</version>
504
<configuration>
505
<groups>com.example.FastTests</groups>
506
<excludedGroups>com.example.SlowTests</excludedGroups>
507
</configuration>
508
</plugin>
509
</plugins>
510
</build>
511
```
512
513
```bash
514
# Run specific categories from command line
515
mvn test -Dgroups=com.example.FastTests
516
mvn test -DexcludedGroups=com.example.SlowTests
517
mvn test -Dgroups=com.example.UnitTests,com.example.FastTests
518
```
519
520
## Gradle Integration
521
522
Run specific categories from Gradle.
523
524
```groovy
525
// build.gradle configuration
526
test {
527
useJUnit {
528
includeCategories 'com.example.FastTests'
529
excludeCategories 'com.example.SlowTests'
530
}
531
}
532
533
// Custom test tasks for different categories
534
task fastTests(type: Test) {
535
useJUnit {
536
includeCategories 'com.example.FastTests'
537
}
538
}
539
540
task integrationTests(type: Test) {
541
useJUnit {
542
includeCategories 'com.example.IntegrationTests'
543
}
544
}
545
```
546
547
```bash
548
# Run specific test tasks
549
./gradlew fastTests
550
./gradlew integrationTests
551
./gradlew test # All tests
552
```
553
554
## Types
555
556
```java { .api }
557
/**
558
* Base class for test filters
559
*/
560
public abstract class Filter {
561
/**
562
* Returns true if test should run
563
* @param description - Test description
564
* @return true to run test
565
*/
566
public abstract boolean shouldRun(Description description);
567
568
/**
569
* Returns description of filter
570
* @return Description string
571
*/
572
public abstract String describe();
573
574
/**
575
* Compose filters with AND logic
576
* @param second - Second filter
577
* @return Combined filter
578
*/
579
public Filter intersect(Filter second);
580
581
/**
582
* Create filter that matches anything
583
* @return Filter that allows all tests
584
*/
585
public static Filter matchAll();
586
}
587
588
/**
589
* Factory for creating category filters from command line arguments
590
*/
591
public class CategoryFilterFactory implements FilterFactory {
592
/**
593
* Create filter from factory parameters
594
* @param params - Filter parameters
595
* @return CategoryFilter
596
*/
597
public Filter createFilter(FilterFactoryParams params) throws FilterNotCreatedException;
598
}
599
600
/**
601
* Validates Category annotations
602
*/
603
public class CategoryValidator implements AnnotationValidator {
604
/**
605
* Validate category annotations on test class
606
* @param testClass - Test class to validate
607
* @return List of validation errors
608
*/
609
public List<Exception> validateAnnotatedClass(TestClass testClass);
610
}
611
```
612