Architecture testing framework for Apache Flink test code using ArchUnit
npx @tessl/cli install tessl/maven-org-apache-flink--flink-architecture-tests-test@2.1.00
# Flink Architecture Tests - Test
1
2
Flink Architecture Tests - Test provides architectural testing rules specifically designed for Apache Flink's test code infrastructure. Built on ArchUnit, it enables enforcement of architectural rules and coding standards within Flink's testing ecosystem, helping maintain code quality and architectural integrity across Flink's extensive test suite.
3
4
## Package Information
5
6
- **Package Name**: flink-architecture-tests-test
7
- **Package Type**: maven
8
- **Language**: Java
9
- **Group ID**: org.apache.flink
10
- **Artifact ID**: flink-architecture-tests-test
11
- **Installation**: Add as dependency in Maven `pom.xml`
12
13
```xml
14
<dependency>
15
<groupId>org.apache.flink</groupId>
16
<artifactId>flink-architecture-tests-test</artifactId>
17
<version>2.1.0</version>
18
<scope>test</scope>
19
</dependency>
20
```
21
22
## Core Imports
23
24
```java
25
import org.apache.flink.architecture.TestCodeArchitectureTestBase;
26
import org.apache.flink.architecture.rules.ITCaseRules;
27
import org.apache.flink.architecture.rules.BanJunit4Rules;
28
import org.apache.flink.architecture.common.Conditions;
29
import org.apache.flink.architecture.common.JavaFieldPredicates;
30
import org.apache.flink.architecture.common.Predicates;
31
import org.apache.flink.architecture.common.ImportOptions;
32
import com.tngtech.archunit.junit.ArchTest;
33
import com.tngtech.archunit.junit.ArchTests;
34
import com.tngtech.archunit.junit.AnalyzeClasses;
35
import com.tngtech.archunit.core.importer.ImportOption;
36
```
37
38
## Basic Usage
39
40
### Setting up Architectural Tests in a Flink Submodule
41
42
```java
43
package org.apache.flink.architecture;
44
45
import com.tngtech.archunit.junit.AnalyzeClasses;
46
import com.tngtech.archunit.junit.ArchTest;
47
import com.tngtech.archunit.junit.ArchTests;
48
import com.tngtech.archunit.core.importer.ImportOption;
49
import org.apache.flink.architecture.TestCodeArchitectureTestBase;
50
import org.apache.flink.architecture.common.ImportOptions;
51
52
@AnalyzeClasses(
53
packages = {"org.apache.flink.your.module"},
54
importOptions = {
55
ImportOption.OnlyIncludeTests.class,
56
ImportOptions.ExcludeScalaImportOption.class,
57
ImportOptions.ExcludeShadedImportOption.class
58
}
59
)
60
public class TestCodeArchitectureTest {
61
62
// Include common architectural tests from the base
63
@ArchTest
64
public static final ArchTests COMMON_TESTS =
65
ArchTests.in(TestCodeArchitectureTestBase.class);
66
}
67
```
68
69
### Configuration Files Setup
70
71
Create `archunit.properties` in `src/test/resources/`:
72
73
```properties
74
# Controls if the violation store is writable (enables updating existing violations)
75
freeze.store.default.allowStoreUpdate=true
76
77
# Path to store violations relative to project root
78
freeze.store.default.path=archunit-violations
79
80
# Allow creation of new violation stores (uncomment when setting up new tests)
81
# freeze.store.default.allowStoreCreation=true
82
83
# Re-freeze all violations to current state (uncomment for rule changes)
84
# freeze.refreeze=true
85
```
86
87
Create `log4j2-test.properties` in `src/test/resources/` for logging configuration during tests:
88
89
```properties
90
# Set root logger to OFF to prevent flooding build logs during architectural testing
91
rootLogger.level=OFF
92
```
93
94
## Architecture
95
96
Flink Architecture Tests - Test is built around several key components:
97
98
- **Base Test Class**: `TestCodeArchitectureTestBase` provides a central setup for common architectural tests
99
- **Rule Collections**: Modular rule classes (`ITCaseRules`, `BanJunit4Rules`) that group related architectural constraints
100
- **Utility Classes**: Base utility classes (`Conditions`, `JavaFieldPredicates`, `Predicates`) for building custom rules
101
- **Import Options**: Custom import filtering (`ImportOptions`) to exclude Scala and shaded classes from analysis
102
- **ArchUnit Integration**: Built on top of ArchUnit framework for Java architectural testing
103
- **Violation Storage**: Freezing mechanism to track and manage architectural violations over time
104
- **Modular Execution**: Rules are executed in individual submodules rather than centrally
105
106
## Capabilities
107
108
### Base Test Class Integration
109
110
Central setup class that provides common architectural tests for all Flink submodules.
111
112
```java { .api }
113
public class TestCodeArchitectureTestBase {
114
@ArchTest
115
public static final ArchTests ITCASE = ArchTests.in(ITCaseRules.class);
116
}
117
```
118
119
Use this class to include standard architectural tests in your submodule:
120
121
```java
122
import com.tngtech.archunit.junit.ArchTest;
123
import com.tngtech.archunit.junit.ArchTests;
124
import org.apache.flink.architecture.TestCodeArchitectureTestBase;
125
126
public class TestCodeArchitectureTest {
127
@ArchTest
128
public static final ArchTests COMMON_TESTS =
129
ArchTests.in(TestCodeArchitectureTestBase.class);
130
}
131
```
132
133
### Integration Test Rules
134
135
Enforces naming conventions and MiniCluster resource usage for integration tests.
136
137
```java { .api }
138
public class ITCaseRules {
139
@ArchTest
140
public static final ArchRule INTEGRATION_TEST_ENDING_WITH_ITCASE;
141
142
@ArchTest
143
public static final ArchRule ITCASE_USE_MINICLUSTER;
144
}
145
```
146
147
**INTEGRATION_TEST_ENDING_WITH_ITCASE**: Ensures that classes inheriting from `AbstractTestBase` have names ending with "ITCase".
148
149
**ITCASE_USE_MINICLUSTER**: Validates that ITCase tests use appropriate MiniCluster resources. Has different requirements for runtime vs non-runtime packages:
150
151
**For runtime packages** (`org.apache.flink.runtime.*`):
152
- Must use `InternalMiniClusterExtension` for JUnit 5
153
- Must use `MiniClusterWithClientResource` for JUnit 4
154
155
**For non-runtime packages**:
156
- Must use `MiniClusterExtension` for JUnit 5
157
- Must use `MiniClusterWithClientResource` for JUnit 4
158
159
JUnit 5 examples:
160
```java
161
// For runtime packages
162
@RegisterExtension
163
public static final InternalMiniClusterExtension MINI_CLUSTER_RESOURCE =
164
new InternalMiniClusterExtension(
165
new MiniClusterResourceConfiguration.Builder()
166
.setConfiguration(getFlinkConfiguration())
167
.build());
168
169
// For non-runtime packages
170
@RegisterExtension
171
public static final MiniClusterExtension MINI_CLUSTER_RESOURCE =
172
new MiniClusterExtension(
173
new MiniClusterResourceConfiguration.Builder()
174
.setConfiguration(getFlinkConfiguration())
175
.build());
176
177
// Alternative annotation-based approach
178
@ExtendWith(MiniClusterExtension.class)
179
public class MyITCase {
180
// test methods
181
}
182
```
183
184
JUnit 4 examples (legacy):
185
```java
186
@Rule
187
public final MiniClusterWithClientResource miniClusterResource =
188
new MiniClusterWithClientResource(
189
new MiniClusterResourceConfiguration.Builder()
190
.setNumberTaskManagers(1)
191
.setNumberSlotsPerTaskManager(PARALLELISM)
192
.build());
193
194
@ClassRule
195
public static final MiniClusterWithClientResource miniClusterResource =
196
new MiniClusterWithClientResource(
197
new MiniClusterResourceConfiguration.Builder()
198
.setNumberTaskManagers(1)
199
.setNumberSlotsPerTaskManager(PARALLELISM)
200
.build());
201
```
202
203
### JUnit 4 Migration Rules
204
205
Enforces migration from JUnit 4 to JUnit 5 by preventing new JUnit 4 dependencies.
206
207
```java { .api }
208
public class BanJunit4Rules {
209
@ArchTest
210
public static final ArchRule NO_NEW_ADDED_JUNIT4_TEST_RULE;
211
}
212
```
213
214
**NO_NEW_ADDED_JUNIT4_TEST_RULE**: Prevents classes in `org.apache.flink..` packages from depending on JUnit 4 classes (`junit`, `org.junit` packages). Uses freezing mechanism to track existing violations while preventing new ones.
215
216
### Utility Classes for Custom Rules
217
218
Core utility classes from `flink-architecture-tests-base` for building custom architectural rules.
219
220
#### Conditions
221
222
Generic conditions for testing architectural properties.
223
224
```java { .api }
225
public class Conditions {
226
/**
227
* Creates condition to check fulfillment of predicates
228
*/
229
public static <T> ArchCondition<T> fulfill(DescribedPredicate<T> predicate);
230
231
/**
232
* Tests leaf types of methods (return types, parameter types, exception types)
233
*/
234
public static ArchCondition<JavaMethod> haveLeafTypes(DescribedPredicate<JavaClass> predicate);
235
236
/**
237
* Tests leaf return types of methods
238
*/
239
public static ArchCondition<JavaMethod> haveLeafReturnTypes();
240
241
/**
242
* Tests leaf argument types of methods
243
*/
244
public static ArchCondition<JavaMethod> haveLeafArgumentTypes();
245
246
/**
247
* Tests leaf exception types of methods
248
*/
249
public static ArchCondition<JavaMethod> haveLeafExceptionTypes();
250
}
251
```
252
253
#### JavaFieldPredicates
254
255
Predicates for testing Java field properties.
256
257
```java { .api }
258
public class JavaFieldPredicates {
259
// Field modifier predicates
260
public static DescribedPredicate<JavaField> isPublic();
261
public static DescribedPredicate<JavaField> isStatic();
262
public static DescribedPredicate<JavaField> isNotStatic();
263
public static DescribedPredicate<JavaField> isFinal();
264
265
// Type matching predicates
266
public static DescribedPredicate<JavaField> ofType(Class<?> type);
267
public static DescribedPredicate<JavaField> ofType(String typeName);
268
public static DescribedPredicate<JavaField> isAssignableTo(Class<?> type);
269
270
// Annotation predicates
271
public static DescribedPredicate<JavaField> annotatedWith(Class<? extends Annotation> annotationType);
272
public static DescribedPredicate<JavaField> annotatedWith(String annotationTypeName);
273
}
274
```
275
276
#### Predicates
277
278
Complex field predicates combining multiple constraints.
279
280
```java { .api }
281
public class Predicates {
282
// Combined field predicates
283
public static DescribedPredicate<JavaField> arePublicStaticOfType(Class<?> type);
284
public static DescribedPredicate<JavaField> arePublicFinalOfType(Class<?> type);
285
public static DescribedPredicate<JavaField> arePublicStaticFinalAssignableTo(Class<?> type);
286
287
// Annotation combination predicates
288
public static DescribedPredicate<JavaField> arePublicFinalOfTypeWithAnnotation(
289
Class<?> type, Class<? extends Annotation> annotationType);
290
public static DescribedPredicate<JavaField> arePublicStaticFinalOfTypeWithAnnotation(
291
Class<?> type, Class<? extends Annotation> annotationType);
292
public static DescribedPredicate<JavaField> areStaticFinalOfTypeWithAnnotation(
293
Class<?> type, Class<? extends Annotation> annotationType);
294
295
// Utility methods
296
public static String getClassSimpleNameFromFqName(String fullyQualifiedName);
297
public static <T> DescribedPredicate<T> exactlyOneOf(DescribedPredicate<? super T>... predicates);
298
}
299
```
300
301
#### ImportOptions
302
303
Custom import filtering options for ArchUnit class analysis.
304
305
```java { .api }
306
public class ImportOptions {
307
/**
308
* Excludes Scala classes from analysis
309
*/
310
public static class ExcludeScalaImportOption implements ImportOption;
311
312
/**
313
* Excludes shaded/relocated classes from analysis
314
*/
315
public static class ExcludeShadedImportOption implements ImportOption;
316
317
/**
318
* Imports only main classes (excludes test classes)
319
*/
320
public static class MavenMainClassesOnly implements ImportOption;
321
}
322
```
323
324
### Rule Management Commands
325
326
When modifying existing rules, regenerate violation stores:
327
328
```bash
329
# Remove existing violation stores
330
rm -rf `find . -type d -name archunit-violations`
331
332
# Regenerate stores with current state
333
mvn test -Dtest="*TestCodeArchitectureTest*" \
334
-DfailIfNoTests=false \
335
-Darchunit.freeze.refreeze=true \
336
-Darchunit.freeze.store.default.allowStoreCreation=true \
337
-Dfast
338
```
339
340
## Types
341
342
### ArchUnit Core Types
343
344
```java { .api }
345
// From ArchUnit framework
346
import com.tngtech.archunit.junit.ArchTest;
347
import com.tngtech.archunit.junit.ArchTests;
348
import com.tngtech.archunit.junit.AnalyzeClasses;
349
import com.tngtech.archunit.lang.ArchRule;
350
import com.tngtech.archunit.lang.ArchCondition;
351
import com.tngtech.archunit.base.DescribedPredicate;
352
import com.tngtech.archunit.core.domain.JavaClass;
353
import com.tngtech.archunit.core.domain.JavaField;
354
import com.tngtech.archunit.core.domain.JavaMethod;
355
import com.tngtech.archunit.core.importer.ImportOption;
356
357
// Annotation for marking architectural test fields
358
@interface ArchTest {
359
}
360
361
// Container for multiple architectural tests
362
class ArchTests {
363
static ArchTests in(Class<?> testClass);
364
}
365
366
// Individual architectural rule
367
interface ArchRule {
368
ArchRule allowEmptyShould(boolean allow);
369
ArchRule as(String description);
370
}
371
372
// Annotation for specifying which classes to analyze
373
@interface AnalyzeClasses {
374
String[] packages();
375
Class<? extends ImportOption>[] importOptions() default {};
376
}
377
378
// Base types for predicates and conditions
379
interface DescribedPredicate<T> {
380
boolean test(T input);
381
DescribedPredicate<T> and(DescribedPredicate<? super T> other);
382
DescribedPredicate<T> or(DescribedPredicate<? super T> other);
383
}
384
385
interface ArchCondition<T> {
386
ArchCondition<T> and(ArchCondition<? super T> condition);
387
ArchCondition<T> or(ArchCondition<? super T> condition);
388
ArchCondition<T> as(String description);
389
}
390
391
// Import filtering interface
392
interface ImportOption {
393
boolean includes(Location location);
394
}
395
```
396
397
### Configuration Properties
398
399
The package uses ArchUnit's configuration system through `archunit.properties`:
400
401
- `freeze.store.default.allowStoreUpdate`: Controls if violation store is writable
402
- `freeze.store.default.allowStoreCreation`: Enables creation of new violation stores
403
- `freeze.refreeze`: Records current state of violations
404
- `freeze.store.default.path`: Path to store violations (default: "archunit-violations")
405
406
## Error Handling
407
408
When architectural tests fail, ArchUnit provides detailed violation reports including:
409
410
- Specific classes that violate the rules
411
- Line numbers and method details where applicable
412
- Clear descriptions of what constraint was violated
413
- Suggestions for fixing the violations
414
415
Common failure scenarios:
416
417
- **ITCase naming violations**: Tests inheriting from `AbstractTestBase` without "ITCase" suffix
418
- **Missing MiniCluster resources**: ITCase tests without proper cluster setup
419
- **Wrong MiniCluster type**: Runtime packages using `MiniClusterExtension` instead of `InternalMiniClusterExtension`
420
- **JUnit 4 dependencies**: New test code depending on legacy JUnit 4 classes
421
- **Configuration issues**: Missing or incorrect `archunit.properties` setup
422
- **Freezing violations**: Rules that have existing violations must be wrapped in `FreezingArchRule.freeze()`
423
424
### Troubleshooting
425
426
**Violation Store Issues:**
427
- If tests fail due to missing violation stores, set `freeze.store.default.allowStoreCreation=true` in `archunit.properties`
428
- To regenerate all violation stores after rule changes, use the `freeze.refreeze=true` option
429
- Violation stores are stored in the `archunit-violations` directory and should be committed to version control
430
431
**Rule Freezing Requirements:**
432
- Rules with existing violations must use `FreezingArchRule.freeze()` wrapper
433
- Frozen rules should have fixed descriptions using `.as(String)` to reduce maintenance overhead
434
- When modifying rule descriptions, regenerate violation stores to prevent false failures
435
436
**Package-Specific Rule Failures:**
437
- Runtime package tests must use `InternalMiniClusterExtension` for JUnit 5
438
- Non-runtime package tests must use `MiniClusterExtension` for JUnit 5
439
- Both can use `MiniClusterWithClientResource` for JUnit 4 compatibility