0
# Field and Reflection Support
1
2
Utilities for waiting on private field values and object state changes using reflection, ideal for testing internal component state.
3
4
## Capabilities
5
6
### Field Access Entry Points
7
8
Create field supplier builders for accessing object fields via reflection.
9
10
#### Instance Field Access
11
12
```java { .api }
13
/**
14
* Create field supplier builder for instance fields
15
* @param object instance containing the field to monitor
16
* @return FieldSupplierBuilder for field specification
17
*/
18
static FieldSupplierBuilder fieldIn(Object object);
19
```
20
21
#### Static Field Access
22
23
```java { .api }
24
/**
25
* Create field supplier builder for static fields
26
* @param clazz class containing the static field to monitor
27
* @return FieldSupplierBuilder for field specification
28
*/
29
static FieldSupplierBuilder fieldIn(Class<?> clazz);
30
```
31
32
### Field Supplier Builder
33
34
Builder interface for specifying field selection criteria and creating callable suppliers.
35
36
#### Type-Based Field Selection
37
38
```java { .api }
39
/**
40
* Specify expected field type for selection and create a supplier
41
* @param fieldType class of the field type (generic parameter preserved)
42
* @return NameAndAnnotationFieldSupplier for additional criteria or direct usage
43
*/
44
<T> NameAndAnnotationFieldSupplier<T> ofType(Class<T> fieldType);
45
```
46
47
### Field Supplier Interfaces
48
49
The field access API provides nested supplier classes that implement `Callable<T>` for different selection criteria combinations.
50
51
#### NameAndAnnotationFieldSupplier
52
53
Primary supplier class returned by `ofType()` that supports additional field narrowing criteria.
54
55
```java { .api }
56
interface NameAndAnnotationFieldSupplier<T> extends Callable<T> {
57
/**
58
* Narrow field selection by field name
59
* @param fieldName the exact name of the field
60
* @return AnnotationFieldSupplier for optional annotation criteria
61
*/
62
AnnotationFieldSupplier<T> andWithName(String fieldName);
63
64
/**
65
* Narrow field selection by annotation type
66
* @param annotationType the annotation class that must be present on the field
67
* @return NameFieldSupplier for optional name criteria
68
*/
69
NameFieldSupplier<T> andAnnotatedWith(Class<? extends Annotation> annotationType);
70
71
/**
72
* Use the field supplier directly (finds first field of specified type)
73
* @return the field value
74
*/
75
T call() throws Exception;
76
}
77
```
78
79
#### AnnotationFieldSupplier
80
81
Supplier for fields selected by type and name, with optional annotation criteria.
82
83
```java { .api }
84
interface AnnotationFieldSupplier<T> extends Callable<T> {
85
/**
86
* Further narrow field selection by annotation (requires exact match of name, type, and annotation)
87
* @param annotationType the annotation class that must be present on the field
88
* @return Callable supplier for the precisely specified field
89
*/
90
Callable<T> andAnnotatedWith(Class<? extends Annotation> annotationType);
91
92
/**
93
* Use the field supplier (finds field by name and type)
94
* @return the field value
95
*/
96
T call();
97
}
98
```
99
100
#### NameFieldSupplier
101
102
Supplier for fields selected by type and annotation, with optional name criteria.
103
104
```java { .api }
105
interface NameFieldSupplier<T> extends Callable<T> {
106
/**
107
* Further narrow field selection by field name (requires exact match of annotation, type, and name)
108
* @param fieldName the exact name of the field
109
* @return Callable supplier for the precisely specified field
110
*/
111
Callable<T> andWithName(String fieldName);
112
113
/**
114
* Use the field supplier (finds field by annotation and type)
115
* @return the field value
116
*/
117
T call() throws Exception;
118
}
119
120
#### NameAndAnnotationFieldSupplier
121
122
Primary supplier class that allows chaining name and annotation criteria.
123
124
```java { .api }
125
/**
126
* Add name constraint to field selection
127
* @param fieldName exact name of the field
128
* @return AnnotationFieldSupplier for optional annotation constraint
129
*/
130
AnnotationFieldSupplier<T> andWithName(String fieldName);
131
132
/**
133
* Add annotation constraint to field selection
134
* @param annotationType annotation class that must be present on field
135
* @return NameFieldSupplier for optional name constraint
136
*/
137
NameFieldSupplier<T> andAnnotatedWith(Class<? extends Annotation> annotationType);
138
139
/**
140
* Get field value using type constraint only
141
* @return field value cast to expected type
142
* @throws FieldNotFoundException if no field of specified type found
143
*/
144
T call() throws Exception;
145
```
146
147
#### AnnotationFieldSupplier
148
149
Supplier for fields selected by type and name, allowing annotation constraint.
150
151
```java { .api }
152
/**
153
* Add annotation constraint (returns self for chaining)
154
* @param annotationType annotation class that must be present on field
155
* @return this supplier for call() execution
156
*/
157
Callable<T> andAnnotatedWith(Class<? extends Annotation> annotationType);
158
159
/**
160
* Get field value using type and name constraints
161
* @return field value cast to expected type
162
* @throws FieldNotFoundException if field not found or type mismatch
163
*/
164
T call() throws Exception;
165
```
166
167
#### NameFieldSupplier
168
169
Supplier for fields selected by type and annotation, allowing name constraint.
170
171
```java { .api }
172
/**
173
* Add name constraint (returns anonymous Callable)
174
* @param fieldName exact name of the field
175
* @return Callable for call() execution
176
*/
177
Callable<T> andWithName(String fieldName);
178
179
/**
180
* Get field value using type and annotation constraints
181
* @return field value cast to expected type
182
* @throws FieldNotFoundException if field not found or type mismatch
183
*/
184
T call() throws Exception;
185
```
186
187
## Field Access Patterns
188
189
The field access API supports several selection patterns through method chaining:
190
191
### Pattern 1: Type-Only Access
192
```java
193
// Access field by type only (first field of matching type)
194
await().until(fieldIn(object).ofType(int.class), equalTo(42));
195
```
196
197
### Pattern 2: Type + Name Access
198
```java
199
// Access field by type and name
200
await().until(fieldIn(object).ofType(String.class).andWithName("username"), equalTo("admin"));
201
```
202
203
### Pattern 3: Type + Annotation Access
204
```java
205
// Access field by type and annotation
206
await().until(fieldIn(object).ofType(boolean.class).andAnnotatedWith(MyAnnotation.class), equalTo(true));
207
```
208
209
### Pattern 4: Type + Name + Annotation Access
210
```java
211
// Access field by type, name, and annotation (most specific)
212
await().until(fieldIn(object).ofType(int.class)
213
.andWithName("counter")
214
.andAnnotatedWith(MyAnnotation.class), greaterThan(10));
215
```
216
217
### Pattern 5: Type + Annotation + Name Access
218
```java
219
// Alternative order: type, annotation, then name
220
await().until(fieldIn(object).ofType(int.class)
221
.andAnnotatedWith(MyAnnotation.class)
222
.andWithName("counter"), greaterThan(10));
223
```
224
225
## Static Field Access
226
227
```java
228
// Access static fields using class reference
229
await().until(fieldIn(DatabasePool.class).ofType(int.class).andWithName("activeConnections"), lessThan(10));
230
231
// Access static field by type only
232
await().until(fieldIn(ConfigManager.class).ofType(boolean.class), equalTo(true));
233
```
234
235
## Exception Handling
236
237
Field access can throw several exceptions during reflection operations:
238
239
```java { .api }
240
/**
241
* Thrown when field cannot be found based on specified criteria
242
*/
243
class FieldNotFoundException extends RuntimeException {
244
public FieldNotFoundException(String message);
245
}
246
247
/**
248
* Thrown when multiple fields match criteria and disambiguation is required
249
*/
250
class TooManyFieldsFoundException extends RuntimeException {
251
public TooManyFieldsFoundException(String message);
252
}
253
```
254
255
### Common Exception Scenarios
256
257
```java
258
try {
259
await().until(fieldIn(object).ofType(String.class), equalTo("expected"));
260
} catch (ConditionTimeoutException e) {
261
// Timeout occurred before field reached expected value
262
if (e.getCause() instanceof FieldNotFoundException) {
263
// Field of specified type doesn't exist
264
}
265
}
266
```
267
268
## Advanced Field Access Examples
269
270
### Complex Object Field Navigation
271
272
```java
273
// Wait for nested object field value
274
await().until(fieldIn(userService).ofType(DatabaseConnection.class)
275
.andWithName("connection"),
276
connection -> connection.isActive());
277
278
// Wait for collection field size
279
await().until(fieldIn(cache).ofType(ConcurrentMap.class)
280
.andWithName("entries"),
281
map -> ((Map<?, ?>) map).size() > 100);
282
```
283
284
### Annotation-Based Field Selection
285
286
```java
287
// Wait for field annotated with @Metric
288
await().until(fieldIn(processor).ofType(long.class)
289
.andAnnotatedWith(Metric.class),
290
greaterThan(1000L));
291
292
// Wait for @ConfigValue annotated field to change
293
await().until(fieldIn(configService).ofType(String.class)
294
.andAnnotatedWith(ConfigValue.class)
295
.andWithName("databaseUrl"),
296
not(equalTo("localhost:3306")));
297
```
298
299
### Type Safety and Generics
300
301
```java
302
// Type-safe field access preserves generics
303
AtomicReference<String> result = await().until(
304
fieldIn(asyncService).ofType(AtomicReference.class)
305
.andWithName("result"),
306
ref -> ref.get() != null);
307
308
// Works with complex generic types
309
List<Order> orders = await().until(
310
fieldIn(orderService).ofType(List.class)
311
.andWithName("pendingOrders"),
312
list -> !((List<?>) list).isEmpty());
313
```