CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-robolectric--robolectric

The industry-standard unit testing framework for Android that enables running tests in a simulated Android environment inside a JVM without requiring an emulator or device.

Pending
Overview
Eval results
Files

shadow-system.mddocs/

Shadow System

Comprehensive mock implementations of Android framework classes providing controllable behavior for testing, with the ability to extract shadow objects and manipulate their internal state.

Capabilities

Shadow Access

The primary interface for accessing shadow implementations of Android objects.

/**
 * Generated class providing shadowOf() methods for extracting shadow objects.
 * Generated at compile time by the Robolectric annotation processor.
 */
public class Shadows {
    /**
     * Generic method to extract shadow from any object.
     * Returns the shadow implementation if one exists.
     */
    public static <T> T shadowOf(Object instance);
    
    // Generated methods for each Android framework class:
    // Examples (hundreds of these are generated):
    
    /** Extract ShadowActivity from Activity instance */
    public static ShadowActivity shadowOf(Activity activity);
    
    /** Extract ShadowApplication from Application instance */  
    public static ShadowApplication shadowOf(Application application);
    
    /** Extract ShadowContext from Context instance */
    public static ShadowContext shadowOf(Context context);
    
    /** Extract ShadowView from View instance */
    public static ShadowView shadowOf(View view);
    
    /** Extract ShadowIntent from Intent instance */
    public static ShadowIntent shadowOf(Intent intent);
    
    /** Extract ShadowBundle from Bundle instance */
    public static ShadowBundle shadowOf(Bundle bundle);
    
    /** Extract ShadowResources from Resources instance */
    public static ShadowResources shadowOf(Resources resources);
    
    /** Extract ShadowPackageManager from PackageManager instance */
    public static ShadowPackageManager shadowOf(PackageManager packageManager);
    
    /** Extract ShadowLooper from Looper instance */
    public static ShadowLooper shadowOf(Looper looper);
    
    /** Extract ShadowHandler from Handler instance */
    public static ShadowHandler shadowOf(Handler handler);
    
    // And many more for all Android framework classes...
}

Usage Examples:

import static org.robolectric.Shadows.shadowOf;

// Activity shadow manipulation
Activity activity = Robolectric.setupActivity(MyActivity.class);
ShadowActivity shadowActivity = shadowOf(activity);
shadowActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

// Application shadow access
Application app = RuntimeEnvironment.getApplication();
ShadowApplication shadowApp = shadowOf(app);
shadowApp.grantPermissions("android.permission.CAMERA");

// Intent shadow manipulation
Intent intent = new Intent("my.action");
ShadowIntent shadowIntent = shadowOf(intent);
shadowIntent.setAction("modified.action");

// Looper control (PAUSED mode)
Looper mainLooper = Looper.getMainLooper();
ShadowLooper shadowLooper = shadowOf(mainLooper);
shadowLooper.idle(); // Process all queued tasks

Shadow Annotations

Annotations for creating custom shadow implementations and controlling shadow behavior.

/**
 * Marks a class as shadow implementation of an Android class.
 * Applied to shadow classes that provide mock implementations.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Implements {
    /** The Android class being shadowed */
    Class<?> value();
    
    /** Whether this shadow is public API */
    boolean isInAndroidSdk() default true;
    
    /** Shadow name for conflicts resolution */
    String shadowName() default "";
    
    /** Minimum SDK version this shadow applies to */
    int minSdk() default -1;
    
    /** Maximum SDK version this shadow applies to */
    int maxSdk() default -1;
}
/**
 * Marks methods as shadow implementations that replace real Android methods.
 * Applied to methods in shadow classes.
 */
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)
public @interface Implementation {
    /** Whether this implementation is for final methods */
    boolean isFinalMethod() default false;
}
/**
 * Injects the real Android object being shadowed into shadow class fields.
 * Applied to fields in shadow classes that should receive the real object.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface RealObject {
}
/**
 * Marks methods as shadow resetters that clean up shadow state between tests.  
 * Applied to static methods in shadow classes.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Resetter {
}
/**
 * Marks access to hidden Android APIs that are not in public SDK.
 * Applied to methods accessing non-public Android internals.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface HiddenApi {
}

Custom Shadow Example:

@Implements(MyCustomClass.class)
public class ShadowMyCustomClass {
    
    @RealObject
    private MyCustomClass realObject;
    
    private static boolean staticState;
    private String instanceState;
    
    @Implementation
    public void someMethod() {
        // Custom shadow behavior
        instanceState = "shadowed";
    }
    
    @Implementation
    public static void staticMethod() {
        staticState = true;
    }
    
    @Resetter
    public static void reset() {
        staticState = false;
    }
    
    // Test helper methods
    public String getInstanceState() {
        return instanceState;
    }
    
    public static boolean getStaticState() {
        return staticState;
    }
}

// Usage in test
@Config(shadows = {ShadowMyCustomClass.class})
public class MyTest {
    @Test
    public void testCustomShadow() {
        MyCustomClass obj = new MyCustomClass();
        obj.someMethod();
        
        ShadowMyCustomClass shadow = shadowOf(obj);
        assertThat(shadow.getInstanceState()).isEqualTo("shadowed");
    }
}

AttributeSet Creation

Utilities for creating AttributeSet objects for testing View creation and styling.

public class Robolectric {
    /**
     * @deprecated Use getAttributeSetFromXml(int) instead
     * Allows programmatic creation of AttributeSet for testing Views without XML.
     */
    @Deprecated
    public static AttributeSetBuilder buildAttributeSet();
    
    /** 
     * Creates AttributeSet from XML resource ID.
     * Recommended approach for creating AttributeSets in tests.
     */
    public static AttributeSet getAttributeSetFromXml(int xmlResId);
}
/**
 * @deprecated Use Xml.asAttributeSet(XmlPullParser) instead
 * Builder interface for creating AttributeSets programmatically.
 */
@Deprecated
public interface AttributeSetBuilder {
    /**
     * Set attribute to given value. Value interpreted according to attribute format.
     */
    AttributeSetBuilder addAttribute(@IdRes int resId, String value);
    
    /**
     * Set style attribute value as resource reference.
     */
    AttributeSetBuilder setStyleAttribute(String value);
    
    /**
     * Build AttributeSet with configured attributes.
     */
    AttributeSet build();
}

Usage Examples:

// Modern approach - from XML resource
AttributeSet attrs = Robolectric.getAttributeSetFromXml(R.xml.my_view_attrs);
MyCustomView view = new MyCustomView(context, attrs);

// Legacy approach - programmatic creation (deprecated)
@SuppressWarnings("deprecation")
AttributeSet attrs = Robolectric.buildAttributeSet()
    .addAttribute(R.attr.text, "Hello World")
    .addAttribute(R.attr.textColor, "#FF0000")
    .build();

Shadow Provider System

The underlying system that manages shadow implementations and provides them to the runtime.

/**
 * Interface implemented by generated Shadows class.
 * Provides shadow mappings to the Robolectric runtime.
 */
public interface ShadowProvider {
    /** Returns collection of shadow class mappings */
    Collection<Map.Entry<String, String>> getShadows();
    
    /** Resets all shadow state */
    void reset();
}

The generated Shadows class implements this interface and is automatically discovered by Robolectric using Java's ServiceLoader mechanism.

Shadow Configuration

Controlling which shadows are active for specific tests.

// Via @Config annotation
@Config(shadows = {MyCustomShadow.class, AnotherShadow.class})
public class MyTest {
    // Test uses additional shadows
}

// Via Config.Builder
Config config = new Config.Builder()
    .setShadows(MyCustomShadow.class, AnotherShadow.class)
    .build();

Common Shadow Usage Patterns

Activity Testing

Activity activity = Robolectric.setupActivity(MyActivity.class);
ShadowActivity shadowActivity = shadowOf(activity);

// Control activity state
shadowActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
shadowActivity.receiveResult(new Intent(), Activity.RESULT_OK, new Intent());

// Check activity behavior  
assertThat(shadowActivity.getNextStartedActivity()).hasAction("expected.action");
assertThat(shadowActivity.isFinishing()).isTrue();

Application Testing

Application app = RuntimeEnvironment.getApplication();
ShadowApplication shadowApp = shadowOf(app);

// Grant permissions
shadowApp.grantPermissions("android.permission.CAMERA", "android.permission.LOCATION");

// Check permissions
assertThat(shadowApp.hasPermission("android.permission.CAMERA")).isTrue();

// Control services
shadowApp.setUnbindServiceShouldThrowIllegalArgument(true);

View Testing

View view = new Button(RuntimeEnvironment.getApplication());
ShadowView shadowView = shadowOf(view);

// Control view properties
shadowView.setGlobalVisibleRect(new Rect(0, 0, 100, 50));
shadowView.setLocationOnScreen(10, 20);

// Check view state
assertThat(shadowView.getGlobalVisibleRect()).isEqualTo(new Rect(0, 0, 100, 50));

Intent Testing

Intent intent = new Intent("my.action");
intent.putExtra("key", "value");
ShadowIntent shadowIntent = shadowOf(intent);

// Verify intent construction
assertThat(shadowIntent.getAction()).isEqualTo("my.action");
assertThat(shadowIntent.getStringExtra("key")).isEqualTo("value");

Looper and Threading

// Main looper control (PAUSED mode)
ShadowLooper mainLooper = shadowOf(Looper.getMainLooper());
mainLooper.idle(); // Process all pending tasks
mainLooper.idleFor(Duration.ofSeconds(5)); // Process tasks for 5 seconds

// Background looper
HandlerThread thread = new HandlerThread("background");
thread.start();
Looper backgroundLooper = thread.getLooper();
ShadowLooper shadowBackgroundLooper = shadowOf(backgroundLooper);
shadowBackgroundLooper.idle(); // Process background tasks

Package Manager Testing

PackageManager pm = RuntimeEnvironment.getApplication().getPackageManager();
ShadowPackageManager shadowPm = shadowOf(pm);

// Add packages for testing
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "com.example.test";
shadowPm.addPackage(packageInfo);

// Control permissions
shadowPm.addPermissionInfo(new PermissionInfo());

Best Practices

  1. Import shadowOf Statically: Use import static org.robolectric.Shadows.shadowOf; for cleaner code.

  2. Use Shadows Judiciously: Only use shadows when you need to control or inspect internal Android behavior that's not accessible through public APIs.

  3. Prefer Public APIs: When possible, use public Android APIs instead of shadow manipulation.

  4. Custom Shadows for Gaps: Create custom shadows for third-party libraries or when built-in shadows don't meet your needs.

  5. Reset Shadow State: Use @Resetter methods in custom shadows to ensure clean state between tests.

  6. Shadow Scoping: Use @Config(shadows = {...}) to limit shadow scope to specific tests that need them.

Install with Tessl CLI

npx tessl i tessl/maven-org-robolectric--robolectric

docs

component-lifecycle.md

index.md

looper-threading.md

runtime-environment.md

shadow-system.md

test-runner.md

tile.json