0
# Shadow System
1
2
Comprehensive mock implementations of Android framework classes providing controllable behavior for testing, with the ability to extract shadow objects and manipulate their internal state.
3
4
## Capabilities
5
6
### Shadow Access
7
8
The primary interface for accessing shadow implementations of Android objects.
9
10
```java { .api }
11
/**
12
* Generated class providing shadowOf() methods for extracting shadow objects.
13
* Generated at compile time by the Robolectric annotation processor.
14
*/
15
public class Shadows {
16
/**
17
* Generic method to extract shadow from any object.
18
* Returns the shadow implementation if one exists.
19
*/
20
public static <T> T shadowOf(Object instance);
21
22
// Generated methods for each Android framework class:
23
// Examples (hundreds of these are generated):
24
25
/** Extract ShadowActivity from Activity instance */
26
public static ShadowActivity shadowOf(Activity activity);
27
28
/** Extract ShadowApplication from Application instance */
29
public static ShadowApplication shadowOf(Application application);
30
31
/** Extract ShadowContext from Context instance */
32
public static ShadowContext shadowOf(Context context);
33
34
/** Extract ShadowView from View instance */
35
public static ShadowView shadowOf(View view);
36
37
/** Extract ShadowIntent from Intent instance */
38
public static ShadowIntent shadowOf(Intent intent);
39
40
/** Extract ShadowBundle from Bundle instance */
41
public static ShadowBundle shadowOf(Bundle bundle);
42
43
/** Extract ShadowResources from Resources instance */
44
public static ShadowResources shadowOf(Resources resources);
45
46
/** Extract ShadowPackageManager from PackageManager instance */
47
public static ShadowPackageManager shadowOf(PackageManager packageManager);
48
49
/** Extract ShadowLooper from Looper instance */
50
public static ShadowLooper shadowOf(Looper looper);
51
52
/** Extract ShadowHandler from Handler instance */
53
public static ShadowHandler shadowOf(Handler handler);
54
55
// And many more for all Android framework classes...
56
}
57
```
58
59
**Usage Examples:**
60
61
```java
62
import static org.robolectric.Shadows.shadowOf;
63
64
// Activity shadow manipulation
65
Activity activity = Robolectric.setupActivity(MyActivity.class);
66
ShadowActivity shadowActivity = shadowOf(activity);
67
shadowActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
68
69
// Application shadow access
70
Application app = RuntimeEnvironment.getApplication();
71
ShadowApplication shadowApp = shadowOf(app);
72
shadowApp.grantPermissions("android.permission.CAMERA");
73
74
// Intent shadow manipulation
75
Intent intent = new Intent("my.action");
76
ShadowIntent shadowIntent = shadowOf(intent);
77
shadowIntent.setAction("modified.action");
78
79
// Looper control (PAUSED mode)
80
Looper mainLooper = Looper.getMainLooper();
81
ShadowLooper shadowLooper = shadowOf(mainLooper);
82
shadowLooper.idle(); // Process all queued tasks
83
```
84
85
### Shadow Annotations
86
87
Annotations for creating custom shadow implementations and controlling shadow behavior.
88
89
```java { .api }
90
/**
91
* Marks a class as shadow implementation of an Android class.
92
* Applied to shadow classes that provide mock implementations.
93
*/
94
@Retention(RetentionPolicy.RUNTIME)
95
@Target(ElementType.TYPE)
96
public @interface Implements {
97
/** The Android class being shadowed */
98
Class<?> value();
99
100
/** Whether this shadow is public API */
101
boolean isInAndroidSdk() default true;
102
103
/** Shadow name for conflicts resolution */
104
String shadowName() default "";
105
106
/** Minimum SDK version this shadow applies to */
107
int minSdk() default -1;
108
109
/** Maximum SDK version this shadow applies to */
110
int maxSdk() default -1;
111
}
112
```
113
114
```java { .api }
115
/**
116
* Marks methods as shadow implementations that replace real Android methods.
117
* Applied to methods in shadow classes.
118
*/
119
@Retention(RetentionPolicy.RUNTIME)
120
@Target(ElementType.METHOD)
121
public @interface Implementation {
122
/** Whether this implementation is for final methods */
123
boolean isFinalMethod() default false;
124
}
125
```
126
127
```java { .api }
128
/**
129
* Injects the real Android object being shadowed into shadow class fields.
130
* Applied to fields in shadow classes that should receive the real object.
131
*/
132
@Retention(RetentionPolicy.RUNTIME)
133
@Target(ElementType.FIELD)
134
public @interface RealObject {
135
}
136
```
137
138
```java { .api }
139
/**
140
* Marks methods as shadow resetters that clean up shadow state between tests.
141
* Applied to static methods in shadow classes.
142
*/
143
@Retention(RetentionPolicy.RUNTIME)
144
@Target(ElementType.METHOD)
145
public @interface Resetter {
146
}
147
```
148
149
```java { .api }
150
/**
151
* Marks access to hidden Android APIs that are not in public SDK.
152
* Applied to methods accessing non-public Android internals.
153
*/
154
@Retention(RetentionPolicy.RUNTIME)
155
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
156
public @interface HiddenApi {
157
}
158
```
159
160
**Custom Shadow Example:**
161
162
```java
163
@Implements(MyCustomClass.class)
164
public class ShadowMyCustomClass {
165
166
@RealObject
167
private MyCustomClass realObject;
168
169
private static boolean staticState;
170
private String instanceState;
171
172
@Implementation
173
public void someMethod() {
174
// Custom shadow behavior
175
instanceState = "shadowed";
176
}
177
178
@Implementation
179
public static void staticMethod() {
180
staticState = true;
181
}
182
183
@Resetter
184
public static void reset() {
185
staticState = false;
186
}
187
188
// Test helper methods
189
public String getInstanceState() {
190
return instanceState;
191
}
192
193
public static boolean getStaticState() {
194
return staticState;
195
}
196
}
197
198
// Usage in test
199
@Config(shadows = {ShadowMyCustomClass.class})
200
public class MyTest {
201
@Test
202
public void testCustomShadow() {
203
MyCustomClass obj = new MyCustomClass();
204
obj.someMethod();
205
206
ShadowMyCustomClass shadow = shadowOf(obj);
207
assertThat(shadow.getInstanceState()).isEqualTo("shadowed");
208
}
209
}
210
```
211
212
### AttributeSet Creation
213
214
Utilities for creating AttributeSet objects for testing View creation and styling.
215
216
```java { .api }
217
public class Robolectric {
218
/**
219
* @deprecated Use getAttributeSetFromXml(int) instead
220
* Allows programmatic creation of AttributeSet for testing Views without XML.
221
*/
222
@Deprecated
223
public static AttributeSetBuilder buildAttributeSet();
224
225
/**
226
* Creates AttributeSet from XML resource ID.
227
* Recommended approach for creating AttributeSets in tests.
228
*/
229
public static AttributeSet getAttributeSetFromXml(int xmlResId);
230
}
231
```
232
233
```java { .api }
234
/**
235
* @deprecated Use Xml.asAttributeSet(XmlPullParser) instead
236
* Builder interface for creating AttributeSets programmatically.
237
*/
238
@Deprecated
239
public interface AttributeSetBuilder {
240
/**
241
* Set attribute to given value. Value interpreted according to attribute format.
242
*/
243
AttributeSetBuilder addAttribute(@IdRes int resId, String value);
244
245
/**
246
* Set style attribute value as resource reference.
247
*/
248
AttributeSetBuilder setStyleAttribute(String value);
249
250
/**
251
* Build AttributeSet with configured attributes.
252
*/
253
AttributeSet build();
254
}
255
```
256
257
**Usage Examples:**
258
259
```java
260
// Modern approach - from XML resource
261
AttributeSet attrs = Robolectric.getAttributeSetFromXml(R.xml.my_view_attrs);
262
MyCustomView view = new MyCustomView(context, attrs);
263
264
// Legacy approach - programmatic creation (deprecated)
265
@SuppressWarnings("deprecation")
266
AttributeSet attrs = Robolectric.buildAttributeSet()
267
.addAttribute(R.attr.text, "Hello World")
268
.addAttribute(R.attr.textColor, "#FF0000")
269
.build();
270
```
271
272
### Shadow Provider System
273
274
The underlying system that manages shadow implementations and provides them to the runtime.
275
276
```java { .api }
277
/**
278
* Interface implemented by generated Shadows class.
279
* Provides shadow mappings to the Robolectric runtime.
280
*/
281
public interface ShadowProvider {
282
/** Returns collection of shadow class mappings */
283
Collection<Map.Entry<String, String>> getShadows();
284
285
/** Resets all shadow state */
286
void reset();
287
}
288
```
289
290
The generated `Shadows` class implements this interface and is automatically discovered by Robolectric using Java's ServiceLoader mechanism.
291
292
### Shadow Configuration
293
294
Controlling which shadows are active for specific tests.
295
296
```java { .api }
297
// Via @Config annotation
298
@Config(shadows = {MyCustomShadow.class, AnotherShadow.class})
299
public class MyTest {
300
// Test uses additional shadows
301
}
302
303
// Via Config.Builder
304
Config config = new Config.Builder()
305
.setShadows(MyCustomShadow.class, AnotherShadow.class)
306
.build();
307
```
308
309
## Common Shadow Usage Patterns
310
311
### Activity Testing
312
313
```java
314
Activity activity = Robolectric.setupActivity(MyActivity.class);
315
ShadowActivity shadowActivity = shadowOf(activity);
316
317
// Control activity state
318
shadowActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
319
shadowActivity.receiveResult(new Intent(), Activity.RESULT_OK, new Intent());
320
321
// Check activity behavior
322
assertThat(shadowActivity.getNextStartedActivity()).hasAction("expected.action");
323
assertThat(shadowActivity.isFinishing()).isTrue();
324
```
325
326
### Application Testing
327
328
```java
329
Application app = RuntimeEnvironment.getApplication();
330
ShadowApplication shadowApp = shadowOf(app);
331
332
// Grant permissions
333
shadowApp.grantPermissions("android.permission.CAMERA", "android.permission.LOCATION");
334
335
// Check permissions
336
assertThat(shadowApp.hasPermission("android.permission.CAMERA")).isTrue();
337
338
// Control services
339
shadowApp.setUnbindServiceShouldThrowIllegalArgument(true);
340
```
341
342
### View Testing
343
344
```java
345
View view = new Button(RuntimeEnvironment.getApplication());
346
ShadowView shadowView = shadowOf(view);
347
348
// Control view properties
349
shadowView.setGlobalVisibleRect(new Rect(0, 0, 100, 50));
350
shadowView.setLocationOnScreen(10, 20);
351
352
// Check view state
353
assertThat(shadowView.getGlobalVisibleRect()).isEqualTo(new Rect(0, 0, 100, 50));
354
```
355
356
### Intent Testing
357
358
```java
359
Intent intent = new Intent("my.action");
360
intent.putExtra("key", "value");
361
ShadowIntent shadowIntent = shadowOf(intent);
362
363
// Verify intent construction
364
assertThat(shadowIntent.getAction()).isEqualTo("my.action");
365
assertThat(shadowIntent.getStringExtra("key")).isEqualTo("value");
366
```
367
368
### Looper and Threading
369
370
```java
371
// Main looper control (PAUSED mode)
372
ShadowLooper mainLooper = shadowOf(Looper.getMainLooper());
373
mainLooper.idle(); // Process all pending tasks
374
mainLooper.idleFor(Duration.ofSeconds(5)); // Process tasks for 5 seconds
375
376
// Background looper
377
HandlerThread thread = new HandlerThread("background");
378
thread.start();
379
Looper backgroundLooper = thread.getLooper();
380
ShadowLooper shadowBackgroundLooper = shadowOf(backgroundLooper);
381
shadowBackgroundLooper.idle(); // Process background tasks
382
```
383
384
### Package Manager Testing
385
386
```java
387
PackageManager pm = RuntimeEnvironment.getApplication().getPackageManager();
388
ShadowPackageManager shadowPm = shadowOf(pm);
389
390
// Add packages for testing
391
PackageInfo packageInfo = new PackageInfo();
392
packageInfo.packageName = "com.example.test";
393
shadowPm.addPackage(packageInfo);
394
395
// Control permissions
396
shadowPm.addPermissionInfo(new PermissionInfo());
397
```
398
399
## Best Practices
400
401
1. **Import shadowOf Statically**: Use `import static org.robolectric.Shadows.shadowOf;` for cleaner code.
402
403
2. **Use Shadows Judiciously**: Only use shadows when you need to control or inspect internal Android behavior that's not accessible through public APIs.
404
405
3. **Prefer Public APIs**: When possible, use public Android APIs instead of shadow manipulation.
406
407
4. **Custom Shadows for Gaps**: Create custom shadows for third-party libraries or when built-in shadows don't meet your needs.
408
409
5. **Reset Shadow State**: Use `@Resetter` methods in custom shadows to ensure clean state between tests.
410
411
6. **Shadow Scoping**: Use `@Config(shadows = {...})` to limit shadow scope to specific tests that need them.