or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

component-lifecycle.mdindex.mdlooper-threading.mdruntime-environment.mdshadow-system.mdtest-runner.md

shadow-system.mddocs/

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.