From 72af34ad26b809084bc7bd19ecac9b4e23094bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Sat, 21 Sep 2024 16:20:58 +0200 Subject: [PATCH] Update the "Migration Guide" page (#312) --- docs/migrating.md | 480 ++++++++++++++++++++++++++++------------------ 1 file changed, 298 insertions(+), 182 deletions(-) diff --git a/docs/migrating.md b/docs/migrating.md index 7298ff493..644439a48 100644 --- a/docs/migrating.md +++ b/docs/migrating.md @@ -10,250 +10,318 @@ Robolectric provides an [automated migration tool](automated-migration.md) to he ### Project Configuration -Robolectric 4.0 requires Android Gradle Plugin / Android Studio 3.2 or greater. - -Update settings in your `build.gradle`: -```groovy -android { - compileSdkVersion 28 - testOptions.unitTests.includeAndroidResources = true -} -``` +Robolectric 4.0 requires Android Gradle Plugin 3.2 or greater. + +Update the configuration in your module's `build.gradle`/`build.gradle.kts` file: + +=== "Groovy" + + ```groovy + android { + compileSdkVersion 28 // Or newer + testOptions.unitTests.includeAndroidResources = true + } + ``` + +=== "Kotlin" + + ```kotlin + android { + compileSdkVersion = 28 // Or newer + testOptions.unitTests.isIncludeAndroidResources = true + } + ``` + +Add the following in your `gradle.properties` file: -Put this in your `gradle.properties`: ```properties android.enableUnitTestBinaryResources=true ``` -If you have dependencies on `com.android.support.test`, switch them to `androidx.test`; see [Migrating to AndroidX](https://developer.android.com/jetpack/androidx/migrate). +If you have dependencies on `com.android.support.test`, switch them to `androidx.test`; see [Migrate to AndroidX](https://developer.android.com/jetpack/androidx/migrate). ### Deprecations -| 3.8 | 4.0 | -| ------------------------------------------------------- | -------------------------------------------------------------- | -| `ShadowApplication.getInstance()` | `RuntimeEnvironment.application` | -| `ShadowApplication.getLatestAlertDialog()` | `ShadowAlertDialog.getLatestAlertDialog()` | -| `ShadowApplication.getLatestDialog()` | `ShadowDialog.getLatestDialog()` | -| `ShadowApplication.getLatestPopupMenu()` | `ShadowPopupMenu.getLatestPopupMenu()` | -| `ShadowLooper.getShadowMainLooper()` | `shadowOf(Looper.getMainLooper())` | +| 3.8 | 4.0 | +|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------| +| `ShadowApplication.getInstance()` | [`RuntimeEnvironment.application`](javadoc/latest/org/robolectric/RuntimeEnvironment.html#application) | +| `ShadowApplication.getLatestAlertDialog()` | [`ShadowAlertDialog.getLatestAlertDialog()`](javadoc/latest/org/robolectric/shadows/ShadowAlertDialog.html#getLatestAlertDialog()) | +| `ShadowApplication.getLatestDialog()` | [`ShadowDialog.getLatestDialog()`](javadoc/latest/org/robolectric/shadows/ShadowDialog.html#getLatestDialog()) | +| `ShadowApplication.getLatestPopupMenu()` | [`ShadowPopupMenu.getLatestPopupMenu()`](javadoc/latest/org/robolectric/shadows/ShadowPopupMenu.html#getLatestPopupMenu()) | +| `ShadowLooper.getShadowMainLooper()` | [`shadowOf(Looper.getMainLooper())`](javadoc/latest/org/robolectric/Shadows.html#shadowOf(android.os.Looper)) | + +The [automatic migration tool](automated-migration.md) includes a migration to help with this. -The [automatic migration tool](#automated-migration) includes a migration to help with this. +The following attributes of the [`@Config`](javadoc/latest/org/robolectric/annotation/Config.html) annotation are no longer supported when using binary resources mode: -* `@Config(manifest=...)` is no longer supported when using binary resources mode. Instead, Robolectric always uses the merged manifest generated by the Android toolchain. If your test was using a custom manifest you'll need to adapt it to not rely on that. -* `@Config(resourceDir=...)`, `@Config(assetDir=...)`, and `@Config(packageName...)` are no longer supported in binary resources mode. +* [`assetDir`](javadoc/latest/org/robolectric/annotation/Config.html#assetDir()) and [`resourceDir`](javadoc/latest/org/robolectric/annotation/Config.html#resourceDir()): follow the recommended file structure of your build system. +* [`manifest`](javadoc/latest/org/robolectric/annotation/Config.html#manifest()): Robolectric always uses the merged manifest generated by the Android toolchain. If your test was using a custom manifest you'll need to adapt it to not rely on that. +* [`packageName`](javadoc/latest/org/robolectric/annotation/Config.html#packageName()): to change your package name, override the `applicationId` in your build system. ### Improper Use of Shadows -Prior to 4.0, it was possible (but ill-advised) to get the shadow for an Android framework object and invoke framework methods there. This could result in unexpected behavior (e.g. code in overridden methods in subclasses wouldn't be called). Shadow implementation methods are now marked `protected` to guard against this. Always invoke framework methods directly on the Android class. +Prior to Robolectric 4.0, it was possible (but ill-advised) to get the shadow for an Android framework object and invoke framework methods there. This could result in unexpected behavior (e.g., code in overridden methods in subclasses wouldn't be called). Shadow implementation methods are now marked `protected` to guard against this. Always invoke framework methods directly on the Android class. -| 3.8 | 4.0 | -| ------------------------------------------------------- | -------------------------------------------------------------- | -| `shadowOf(activity).finish();` | `activity.finish()` | -| `ShadowSystemClock.currentTimeMillis();` | `SystemClock.currentTimeMillis();` | +| 3.8 | 4.0 | +|------------------------------------------|--------------------------------------------------------------------------------------------------------------| +| `shadowOf(activity).finish();` | [`activity.finish()`](https://developer.android.com/reference/android/app/Activity#finish()) | +| `ShadowSystemClock.currentTimeMillis();` | [`System.currentTimeMillis()`](https://developer.android.com/reference/java/lang/System#currentTimeMillis()) | -The [automatic migration tool](#automated-migration) will fix most of these for you. +The [automatic migration tool](automated-migration.md) will fix most of these for you. -### androidx.test +### `androidx.test` -Robolectric 4.0 includes initial support for [`androidx.test` APIs](https://developer.android.com/reference/androidx/test/packages). We strongly recommend adding `androidx.test:core:1.0.0` as a test dependency and using those APIs whenever possible rather than using Robolectric-specific APIs. +Robolectric 4.0 includes initial support for [`androidx.test` APIs](https://developer.android.com/reference/androidx/test/package-summary). We strongly recommend adding the latest version of `androidx.test:core` as a test dependency and using those APIs whenever possible rather than using Robolectric-specific APIs. -| 3.8 | 4.0 | -| ------------------------------------------------------- | -------------------------------------------------------------- | -| `RuntimeEnvironment.application` | [`ApplicationProvider.getApplicationContext()`](https://developer.android.com/reference/androidx/test/core/app/ApplicationProvider) | -| `ShadowMotionEvent` | [`MotionEventBuilder`](https://developer.android.com/reference/androidx/test/core/view/MotionEventBuilder) | +| 3.8 | 4.0 | +|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `RuntimeEnvironment.application` | [`ApplicationProvider.getApplicationContext()`](https://developer.android.com/reference/androidx/test/core/app/ApplicationProvider#getApplicationContext()) | +| `ShadowMotionEvent` | [`MotionEventBuilder`](https://developer.android.com/reference/androidx/test/core/view/MotionEventBuilder) | ### Troubleshooting -Robolectric 4.0 replaces its old home-grown resource handling code with a direct adaptation of Android's resource handling code, using the full Android toolchain. This greatly improves fidelity to the behavior of a real Android device, but if your tests were relying on quirks of the old code, you may need to fix your tests. +Robolectric 4.0 replaces its old home-grown resource handling code with a direct adaptation of Android's resource handling code, using the full Android toolchain. This greatly improves fidelity to the behavior of a real Android device, but if your tests were relying on the quirks of the old code, you may need to fix your tests. Some likely issues include: -* `android.view.InflateException: Binary XML file line #3: Failed to resolve attribute at index 17: TypedValue{t=0x2/d=0x7f01000e a=-1}`
This happens when your activity is using a theme that lacks values for certain attributes used by layouts. Make sure you've specified an appropriate theme for your activities in the manifest. +* `android.view.InflateException: Binary XML file line #3: Failed to resolve attribute at index 17: TypedValue{t=0x2/d=0x7f01000e a=-1}`
This happens when your [`Activity`](https://developer.android.com/reference/android/app/Activity) is using a theme that lacks values for certain attributes used by layouts. Make sure you've specified an appropriate theme for your activities in your `AndroidManifest`. --- ## Migrating to 3.6 -Previously Robolectric's Display and DisplayMetrics dimensions defaulted to 480px x 800px, regardless of screen size and density. Now they match the requested configuration, which defaults to 320dp x 470dp mdpi. Tests which rely on the old dimensions for layouts, pixel math, etc. can be modified for new dimensions, or by pinning them to the old size: `@Config(qualifiers = "w480dp-h800dp")`. +Previously, Robolectric's [`Display`](https://developer.android.com/reference/android/view/Display) and [`DisplayMetrics`](https://developer.android.com/reference/android/util/DisplayMetrics) dimensions defaulted to 480×800px, regardless of screen size and density. Now they match the requested configuration, which defaults to 320×470dp mdpi. Tests which rely on the old dimensions for layouts, pixel math, etc. can be modified for new dimensions, or by pinning them to the old size: `@Config(qualifiers = "w480dp-h800dp")`. -| 3.5 | 3.6 | -| ------------------------------------------------------- | -------------------------------------------------------------- | -| `Shadow.newInstanceOf(Display.class)` | `ShadowDisplay.getDefaultDisplay()` | +| 3.5 | 3.6 | +|---------------------------------------|----------------------------------------------------------------------------------------------------------------------| +| `Shadow.newInstanceOf(Display.class)` | [`ShadowDisplay.getDefaultDisplay()`](javadoc/latest/org/robolectric/shadows/ShadowDisplay.html#getDefaultDisplay()) | --- ## Migrating to 3.4 ### Dependencies + Robolectric tests should now be compiled with Java 8 and Android API 26. ### PackageManager -We've change `PackageManager` simulation to follow the standard Shadow pattern as with other framework classes. You can use `ShadowPackageManager` where you previously used `RobolectricPackageManager`. -| 3.3 (was `@Deprecated`) | 3.4 | -| ------------------------------------------------------- | -------------------------------------------------------------- | -| `org.robolectric.res.builder.RobolectricPackageManager` | `org.robolectric.shadows.ShadowPackageManager` | -| `RuntimeEnvironment.getRobolectricPackageManager() ` | `shadowOf(RuntimeEnvironment.application.getPackageManager())` | +We've change [`PackageManager`](https://developer.android.com/reference/android/content/pm/PackageManager) simulation to follow the standard Shadow pattern as with other framework classes. You can use [`ShadowPackageManager`](javadoc/latest/org/robolectric/shadows/ShadowPackageManager.html) where you previously used `RobolectricPackageManager`. + +| 3.3 (was `@Deprecated`) | 3.4 | +|---------------------------------------------------------|--------------------------------------------------------------------------------------------| +| `org.robolectric.res.builder.RobolectricPackageManager` | [`ShadowPackageManager`](javadoc/latest/org/robolectric/shadows/ShadowPackageManager.html) | +| `RuntimeEnvironment.getRobolectricPackageManager() ` | `shadowOf(RuntimeEnvironment.application.getPackageManager())` | -If you were using `RuntimeEnvironment.setRobolectricPackageManager()` to install a custom `PackageManager`, you should move your custom behavior to a subclass of `ShadowApplicationPackageManager` and install it as a shadow (using `@Config()` and possibly `@Implements(inheritImplementationMethods=true)`) instead. +If you were using `RuntimeEnvironment.setRobolectricPackageManager()` to install a custom `PackageManager`, you should move your custom behavior to a subclass of [`ShadowApplicationPackageManager`](javadoc/latest/org/robolectric/shadows/ShadowApplicationPackageManager.html) and install it as a shadow (using [`@Config`](javadoc/latest/org/robolectric/annotation/Config.html) and possibly `@Implements(inheritImplementationMethods = true)`) instead. -### ActivityController.attach() -The deprecated and redundant `attach()` method has been removed from `ActivityController`, `FragmentController`, and `ServiceController`. To migrate, simply remove calls to this method. +### `ActivityController.attach()` + +The deprecated and redundant `attach()` method has been removed from [`ActivityController`](javadoc/latest/org/robolectric/android/controller/ActivityController.html), [`FragmentController`](javadoc/latest/org/robolectric/android/controller/FragmentController.html), and [`ServiceController`](javadoc/latest/org/robolectric/android/controller/ServiceController.html). To migrate, remove calls to this method. ### Other Stuff -* Because `SharedPreferences` now uses real Android framework code, Mockito mocks and spies can lead to tests hanging. Rather than spying on `SharedPreferences`, just assert against its state. +* Because [`SharedPreferences`](https://developer.android.com/reference/android/content/SharedPreferences) now uses real Android framework code, [Mockito](https://site.mockito.org/) mocks and spies can lead to tests hanging. Rather than spying on `SharedPreferences`, directly assert against its state. --- ## Migrating to 3.3 ### Moved classes -To simplify classloader logic and clean up packages, some classes have moved. The old class locations are `@Deprecated` and will be removed in 3.4. - -| 3.2 (now `@Deprecated`) | 3.3 | -| ----------------------------------------------------- | ----------------------------------------------------- | -| `org.robolectric.internal.Shadow` | `org.robolectric.shadow.api.Shadow` | -| `org.robolectric.internal.ShadowExtractor.extract()` | `org.robolectric.shadow.api.Shadow.extract()` | -| `org.robolectric.util.ActivityController` | `org.robolectric.android.controller.ActivityController` | -| `org.robolectric.util.ContentProviderController` | `org.robolectric.android.controller.ContentProviderController` | -| `org.robolectric.util.FragmentController` | `org.robolectric.android.controller.FragmentController` | -| `org.robolectric.util.IntentServiceController` | `org.robolectric.android.controller.IntentServiceController` | -| `org.robolectric.util.ServiceController` | `org.robolectric.android.controller.ServiceController` | -| `org.robolectric.util.AccessibilityUtil` | `org.robolectric.android.AccessibilityUtil` | -| `org.robolectric.util.ApplicationTestUtil` | `org.robolectric.android.ApplicationTestUtil` | -| `org.robolectric.res.builder.StubPackageManager` | `org.robolectric.android.StubPackageManager` (but don't use unless you must) | -| `org.robolectric.res.builder.XmlResourceParserImpl` | `org.robolectric.android.XmlResourceParserImpl` (but don't use unless you must) | -| `org.robolectric.internal.fakes.RoboCharsets` | `org.robolectric.android.fakes.RoboCharsets` (but don't use unless you must) | -| `org.robolectric.internal.fakes.RoboExtendedResponseCache` | `org.robolectric.android.fakes.RoboExtendedResponseCache` (but don't use unless you must) | -| `org.robolectric.util.concurrent.RoboExecutorService` | `org.robolectric.android.util.concurrent.RoboExecutorService` (but don't use unless you must) | -### PackageManager -We have begun the process of switching from using a subclass of `PackageManager` towards `PackageManager` being implemented by a standard shadow, as we do for the rest of the framework. This is for a number of reasons: +To simplify classloader logic and cleanup packages, some classes have moved. The old class locations are `@Deprecated` and will be removed in 3.4. + +| 3.2 (now `@Deprecated`) | 3.3 | +|------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| +| `org.robolectric.internal.Shadow` | [`org.robolectric.shadow.api.Shadow`](javadoc/latest/org/robolectric/shadow/api/Shadow.html) | +| `org.robolectric.internal.ShadowExtractor.extract()` | [`org.robolectric.shadow.api.Shadow.extract()`](javadoc/latest/org/robolectric/shadow/api/Shadow.html#extract(java.lang.Object)) | +| `org.robolectric.util.ActivityController` | [`org.robolectric.android.controller.ActivityController`](javadoc/latest/org/robolectric/android/controller/ActivityController.html) | +| `org.robolectric.util.ContentProviderController` | [`org.robolectric.android.controller.ContentProviderController`](javadoc/latest/org/robolectric/android/controller/ContentProviderController.html) | +| `org.robolectric.util.FragmentController` | [`org.robolectric.android.controller.FragmentController`](javadoc/latest/org/robolectric/android/controller/FragmentController.html) | +| `org.robolectric.util.IntentServiceController` | [`org.robolectric.android.controller.IntentServiceController`](javadoc/latest/org/robolectric/android/controller/IntentServiceController.html) | +| `org.robolectric.util.ServiceController` | [`org.robolectric.android.controller.ServiceController`](javadoc/latest/org/robolectric/android/controller/ServiceController.html) | +| `org.robolectric.util.AccessibilityUtil` | `org.robolectric.android.AccessibilityUtil` | +| `org.robolectric.util.ApplicationTestUtil` | `org.robolectric.android.ApplicationTestUtil` | +| `org.robolectric.res.builder.StubPackageManager` | `org.robolectric.android.StubPackageManager` ⚠️ | +| `org.robolectric.res.builder.XmlResourceParserImpl` | [`org.robolectric.android.XmlResourceParserImpl`](javadoc/latest/org/robolectric/android/XmlResourceParserImpl.html) ⚠️ | +| `org.robolectric.internal.fakes.RoboCharsets` | `org.robolectric.android.fakes.RoboCharsets` ⚠️ | +| `org.robolectric.internal.fakes.RoboExtendedResponseCache` | [`org.robolectric.android.fakes.RoboExtendedResponseCache`](javadoc/latest/org/robolectric/fakes/RoboExtendedResponseCache.html) ⚠️ | +| `org.robolectric.util.concurrent.RoboExecutorService` | [`org.robolectric.android.util.concurrent.RoboExecutorService`](javadoc/latest/org/robolectric/android/util/concurrent/RoboExecutorService.html) ⚠️ | + +⚠️ Only use these classes if you really need too + +### `PackageManager` + +We have begun the process of switching from using a subclass of [`PackageManager`](https://developer.android.com/reference/android/content/pm/PackageManager) towards `PackageManager` being implemented by a standard shadow, as we do for the rest of the framework. This is for a number of reasons: * It is more consistent with the way other framework code is handled. * A shadow will allow users' tests to build against any version of Android. * Switching to a shadow will allow us to defer parsing the manifest until the test or code under test makes calls to the `PackageManager`. -This should all be backwards compatible for the 3.3 release but now you can start migrating your code. +This should all be backwards compatible for the 3.3 release, but now you can start migrating your code. -Before, the Robolectric class `DefaultPackageManager` implemented all `PackageManager` functionality. If you wanted to change any of its behavior, you'd extend `DefaultPackageManager` (or `StubPackageManager`) and override whichever methods you liked. Test-related setup was accomplished by calling `RuntimeEnvironment.getRobolectricPackageManager()`, which had extra methods for modifying its behavior. +Before, the Robolectric class `DefaultPackageManager` implemented all `PackageManager` functionality. If you wanted to change any of its behavior, you'd extend `DefaultPackageManager` (or `StubPackageManager`) and override whichever methods you liked. Test-related setup was achieved by calling `RuntimeEnvironment.getRobolectricPackageManager()`, which had extra methods for modifying its behavior. -As of 3.3, Robolectric uses the normal Android `ApplicationPackageManager`, and shadows all of its methods, causing it to delegate to an instance of `DefaultPackageManager`, which works as before. You can still replace it with your own subclass of `PackageManager` if you like, but that's deprecated. Instead of doing that, put your custom behavior in a subclass of `ShadowApplicationPackageManager`. For test-related setup, you can still access it through `RuntimeEnvironment.getRobolectricPackageManager()`, but you should start using `shadowOf(packageManager)` instead. Note that we've implemented quite a bit more of `PackageManager`, so you might not need any custom code any longer. +As of 3.3, Robolectric uses the normal Android `ApplicationPackageManager`, and shadows all of its methods, causing it to delegate to an instance of `DefaultPackageManager`, which works as before. You can still replace it with your own subclass of `PackageManager` if you like, but that's deprecated. Instead of doing that, put your custom behavior in a subclass of [`ShadowApplicationPackageManager`](javadoc/latest/org/robolectric/shadows/ShadowApplicationPackageManager.html). For test-related setup, you can still access it through `RuntimeEnvironment.getRobolectricPackageManager()`, but you should start using [`shadowOf(PackageManager)`](javadoc/latest/org/robolectric/Shadows.html#shadowOf(android.content.pm.PackageManager)) instead. Note that we've implemented quite a bit more of `PackageManager`, so you might not need any custom code any longer. Starting with 3.4, `DefaultPackageManager` will be removed and its functionality will be moved into `ShadowApplicationPackageManager`. -| 3.2 (now `@Deprecated`) | 3.3 | -| ----------------------------------------------------- | ----------------------------------------------------- | -| `RobolectricPackageManager rpm`
` = RuntimeEnvironment.getRobolectricPackageManager();` | `ShadowPackageManager shadowPackageManager`
` = shadowOf(context.getPackageManager());` | -| `PackageManager packageManager`
` = RuntimeEnvironment.getPackageManager();` | `// Prefer Android Framework APIs where possible.`
`PackageManager packageManager = context.getPackageManager();` | -| `RuntimeEnvironment.setRobolectricPackageManager(myCustomPackageManager);` | Use a custom shadow instead! See below. | +| 3.2 (now `@Deprecated`) | 3.3 | +|--------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------| +| `RobolectricPackageManager rpm = RuntimeEnvironment.getRobolectricPackageManager();` | `ShadowPackageManager shadowPackageManager`
` = shadowOf(context.getPackageManager());` | +| `PackageManager packageManager = RuntimeEnvironment.getPackageManager();` | `// Prefer Android Framework APIs where possible`
`PackageManager packageManager = context.getPackageManager();` | +| `RuntimeEnvironment.setRobolectricPackageManager(customPackageManager);` | Use a custom shadow instead! See below. | -Replace subclasses of `DefaultPackageManager` a custom shadow (and be a good citizen and contribute your enhancements upstream :-) +Replace subclasses of `DefaultPackageManager` by a custom shadow (and be a good citizen by contributing your enhancements upstream 🙂): -```java -@Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true) -class MyCustomPackageManager extends ShadowApplicationPackageManager { -} -``` +=== "Java" -If you are using a custom subclass of `DefaultPackageManager` to implement functionality missing in Robolectric, check again as part of this work we've added support for a bunch more widely-used `PackageManager` features and it might be now possible to completely remove your custom subclass. + ```java + @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true) + public class MyCustomPackageManager extends ShadowApplicationPackageManager { + } + ``` -### Deprecated Classes & Methods +=== "Kotlin" + + ```kotlin + @Implements(value = ApplicationPackageManager::class, inheritImplementationMethods = true) + class MyCustomPackageManager : ShadowApplicationPackageManager + ``` -| 3.2 (now `@Deprecated`) | 3.3 | -| ----------------------------------------------------------- | -------------------------------------------------------------- | -| `org.robolectric.res.builder.RobolectricPackageManager` | `org.robolectric.shadows.ShadowPackageManager` | -| `RuntimeEnvironment.getRobolectricPackageManager()` | `shadowOf(RuntimeEnvironment.application.getPackageManager())` | -| `shadowOf(imageView).getImageResourceId()` | `shadowOf(imageView.getDrawable).getCreatedFromResId()` | -| `shadowOf(notification).getProgressBar().isIndeterminate()` | `shadowOf(notification).isIndeterminate()` | +If you are using a custom subclass of `DefaultPackageManager` to implement functionality missing in Robolectric, check again as part of this work we've added support for a bunch more widely-used `PackageManager` features, and it might be now possible to completely remove your custom subclass. +### Deprecated Classes & Methods + +| 3.2 (now `@Deprecated`) | 3.3 | +|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `org.robolectric.res.builder.RobolectricPackageManager` | [`org.robolectric.shadows.ShadowPackageManager`](javadoc/latest/org/robolectric/shadows/ShadowPackageManager.html) | +| `RuntimeEnvironment.getRobolectricPackageManager()` | [`shadowOf(RuntimeEnvironment.application.getPackageManager())`](javadoc/latest/org/robolectric/Shadows.html#shadowOf(android.content.pm.PackageManager)) | +| `shadowOf(imageView).getImageResourceId()` | [`shadowOf(imageView.getDrawable)`](javadoc/latest/org/robolectric/Shadows.html#shadowOf(android.graphics.drawable.Drawable)).[`getCreatedFromResId()`](javadoc/latest/org/robolectric/shadows/ShadowDrawable.html#getCreatedFromResId()) | +| `shadowOf(notification).getProgressBar().isIndeterminate()` | [`shadowOf(notification)`](javadoc/latest/org/robolectric/Shadows.html#shadowOf(android.app.Notification)).[`isIndeterminate()`](javadoc/latest/org/robolectric/shadows/ShadowNotification.html#isIndeterminate()) | The following methods and classes are deprecated will be removed in 3.4: -```java -RuntimeEnvironment.getPackageManager() -RuntimeEnvironment.getRobolectricPackageManager() -RuntimeEnvironment.setRobolectricPackageManager() -DefaultPackageManager -StubPackageManager -RobolectricPackageManager -``` + +* `RuntimeEnvironment.getPackageManager()` +* `RuntimeEnvironment.getRobolectricPackageManager()` +* `RuntimeEnvironment.setRobolectricPackageManager()` +* `DefaultPackageManager` +* `StubPackageManager` +* `RobolectricPackageManager` ### New Gradle integration -Starting with Robolectric 3.3 + Android Studio 3.0 alpha 5 we've included better integration with the tool chain. Resources, assets and `AndroidManifest.xml` are treated as first class citizens and processed by the build system for correctness, performance and stability between releases. Read more [here](getting-started.md). +Starting with Robolectric 3.3 and Android Studio 3.0 alpha 5, we've included better integration with the tool chain. Resources, assets and `AndroidManifest.xml` are treated as first-class citizens and processed by the build system for correctness, performance and stability between releases. Read more [here](getting-started.md). --- ## Migrating to 3.2 ### Programmatic Configuration -If you were providing custom configuration by subclassing and overriding methods on `RobolectricTestRunner`, you'll need to make some changes. - -`RobolectricTestRunner.getConfigProperties()` has moved to `ConfigMerger.getConfigProperties(String)`. But what you probably actually want to do is override `RobolectricTestRunner.buildGlobalConfig()`. - -Old code: -```java -class MyTestRunner extends RobolectricTestRunner { - @Override protected Properties getConfigProperties() { - Properties props = new Properties(); - props.setProperty("sdk", "23"); - return props; - } -} -``` -New code: +If you were providing custom configuration by subclassing and overriding methods on [`RobolectricTestRunner`](javadoc/latest/org/robolectric/RobolectricTestRunner.html), you'll need to make some changes. -```java -class MyTestRunner extends RobolectricTestRunner { - @Override protected Config buildGlobalConfig() { - return new Config.Builder().setSdk(23).build(); - } -} -``` +`RobolectricTestRunner.getConfigProperties()` has moved to [`ConfigMerger.getConfigProperties(String)`](javadoc/latest/org/robolectric/ConfigMerger.html#getConfigProperties(java.lang.String)). But what you probably actually want to do is override [`RobolectricTestRunner.buildGlobalConfig()`](javadoc/latest/org/robolectric/RobolectricTestRunner.html#buildGlobalConfig()). + +#### Old code + +=== "Java" + + ```java + public class MyTestRunner extends RobolectricTestRunner { + @Override protected Properties getConfigProperties() { + Properties props = new Properties(); + props.setProperty("sdk", "23"); + return props; + } + } + ``` + +=== "Kotlin" + + ```kotlin + class MyTestRunner : RobolectricTestRunner { + override protected fun getConfigProperties(): Properties { + val props = Properties() + props.setProperty("sdk", "23") + return props + } + } + ``` + +#### New code + +=== "Java" + + ```java + public class MyTestRunner extends RobolectricTestRunner { + @Override protected Config buildGlobalConfig() { + return new Config.Builder().setSdk(23).build(); + } + } + ``` + +=== "Kotlin" + + ```kotlin + class MyTestRunner : RobolectricTestRunner { + override protected buildGlobalConfig(): Config { + return Config.Builder().setSdk(23).build() + } + } + ``` ### Package-Level Configuration -If you are using `robolectric.properties` file to configure all tests, the expected location of the file has been changed. -- 3.1: `src/test/resources/robolectric.properties` -- 3.2: `src/test/resources/your/package/path/robolectric.properties` +If you are using a [`robolectric.properties`](configuring.md#robolectricproperties-file) file to configure all tests, the expected location of the file has been changed. + +| 3.1 | 3.2 | +|---------------------------------------------|----------------------------------------------------------| +| `src/test/resources/robolectric.properties` | `src/test/resources/your/package/robolectric.properties` | --- ## Migrating to 3.1 ### Changes -* To construct Android components such as `Activity`, `Service` and `ContentProvider` classes you must now use the Robolectric APIs such as `Robolectric.buildService()`, `Robolectric.buildActivity()` or `Robolectric.buildContentProvider()` or the `setup*()` variants; you should not create new instances of these classes yourself. Calling these methods will create an instance of the component and attach its base context. This is now necessary as we've removed code shadowing `Context` and `ContextWrapper` in favor of using real framework code to improve fidelity. -```java -Robolectric.buildService(MyService.class).create().get(); -Robolectric.setupContentProvider(MyContentProvider.class); -``` +* To construct an Android components such as an [`Activity`](https://developer.android.com/reference/android/app/Activity), a [`Service`](https://developer.android.com/reference/android/app/Service) or a [`ContentProvider`](https://developer.android.com/reference/android/content/ContentProvider), you must now use the Robolectric APIs such as [`Robolectric.buildActivity()`](javadoc/latest/org/robolectric/Robolectric.html#buildActivity(java.lang.Class)), [`Robolectric.buildService()`](javadoc/latest/org/robolectric/Robolectric.html#buildService(java.lang.Class)), or [`Robolectric.buildContentProvider()`](javadoc/latest/org/robolectric/Robolectric.html#buildContentProvider(java.lang.Class)), or the corresponding `setup*()` methods; you should not create new instances of these classes yourself. Calling these methods will create an instance of the component and attach its base [`Context`](https://developer.android.com/reference/android/content/Context). This is now necessary as we've removed code shadowing `Context` and [`ContextWrapper`](https://developer.android.com/reference/android/content/ContextWrapper) in favor of using real framework code to improve fidelity. -* We've removed shadow methods where they duplicate the functionality of the Android APIs. In general, prefer calling Android framework APIs over Robolectric shadows where possible. +=== "Java" -| 3.0 | 3.1 | -| ------------- | ------------- | -| `ShadowApplication.getInstance().getContentResolver()` | `RuntimeEnvironment.application.getContentResolver()` | -| `ShadowApplication.getInstance().getPackageManager()` | `RuntimeEnvironment.application.getPackageManager()` | -| `ShadowApplication.getInstance().getResources()` | `RuntimeEnvironment.application.getResources()` | -| `ShadowApplication.getInstance().getString()` | `RuntimeEnvironment.application.getString()` | -| `ShadowApplication.getInstance().resetResources()` | `RuntimeEnvironment.application.resetResources()` | -| `ShadowApplication.getInstance().getAssets()` | `RuntimeEnvironment.application.getAssets()` | -| `ShadowPreferenceManager.getDefaultSharedPreferences()` | `PreferenceManager.getDefaultSharedPreferences()` | -| `shadowOf(shadowOf(notification).getStyle()).getSummaryText()` | `shadowOf(notification).getContentText()` | + ```java + Robolectric.buildService(MyService.class).create().get(); + Robolectric.setupContentProvider(MyContentProvider.class); + ``` -* RoboMenu now requires a context passed into its constructor. Previously it was internally using RuntimeEnvironment.application which you can use as a sensible default. +=== "Kotlin" -* `.equals()` and `.hashCode()` methods have been removed from shadows to stop them incorrectly providing an alternative equality of the shadowed object. For example, `Intent.equals()` now behaves as the framework method. Instead of relying on an generically shadowed `equals()` method with a vague equality rule, prefer to make assertions on specific fields of interest to the test. We recommend using https://github.com/square/assertj-android to make assertions clear. + ```kotlin + Robolectric.buildService(MyService::class).create().get() + Robolectric.setupContentProvider(MyContentProvider::class) + ``` +* We've removed shadow methods where they duplicate the functionality of the Android APIs. In general, prefer calling Android framework APIs over Robolectric shadows where possible. + +| 3.0 | 3.1 | +|----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ShadowApplication.getInstance().getContentResolver()` | [`RuntimeEnvironment.application`](javadoc/latest/org/robolectric/RuntimeEnvironment.html#application).[`getContentResolver()`](https://developer.android.com/reference/android/content/Context#getContentResolver()) | +| `ShadowApplication.getInstance().getPackageManager()` | [`RuntimeEnvironment.application`](javadoc/latest/org/robolectric/RuntimeEnvironment.html#application).[`getPackageManager()`](https://developer.android.com/reference/android/content/Context#getPackageManager()) | +| `ShadowApplication.getInstance().getResources()` | [`RuntimeEnvironment.application`](javadoc/latest/org/robolectric/RuntimeEnvironment.html#application).[`getResources()`](https://developer.android.com/reference/android/content/Context#getResources()) | +| `ShadowApplication.getInstance().getString()` | [`RuntimeEnvironment.application`](javadoc/latest/org/robolectric/RuntimeEnvironment.html#application).[`getString()`](https://developer.android.com/reference/android/content/Context#getString(int)) | +| `ShadowApplication.getInstance().resetResources()` | [`RuntimeEnvironment.application`](javadoc/latest/org/robolectric/RuntimeEnvironment.html#application).`resetResources()` | +| `ShadowApplication.getInstance().getAssets()` | [`RuntimeEnvironment.application`](javadoc/latest/org/robolectric/RuntimeEnvironment.html#application).[`getAssets()`](https://developer.android.com/reference/android/content/Context#getAssets()) | +| `ShadowPreferenceManager.getDefaultSharedPreferences()` | [`PreferenceManager.getDefaultSharedPreferences()`](https://developer.android.com/reference/android/preference/PreferenceManager#getDefaultSharedPreferences(android.content.Context)) | +| `shadowOf(shadowOf(notification).getStyle()).getSummaryText()` | [`shadowOf(notification)`](javadoc/latest/org/robolectric/Shadows.html#shadowOf(android.app.Notification)).[`getContentText()`](javadoc/latest/org/robolectric/shadows/ShadowNotification.html#getContentText()) | + +* [`RoboMenu`](javadoc/latest/org/robolectric/fakes/RoboMenu.html) now requires a `Context` passed into its constructor. Previously it was internally using [`RuntimeEnvironment.application`](javadoc/latest/org/robolectric/RuntimeEnvironment.html#application) which you can use as a sensible default. +* `.equals()` and `.hashCode()` methods have been removed from shadows to stop them incorrectly providing an alternative equality of the shadowed object. For example, `Intent.equals()` now behaves as the framework method. Instead of relying on a generically shadowed `equals()` method with a vague equality rule, prefer to make assertions on specific fields of interest to the test. We recommend using [`square/assertj-android`](https://github.com/square/assertj-android) to make assertions clear. * Custom shadows now require the `public` access modifier on methods in the shadow class. ### 3.1.1 Changes -* RoboAttributeSet is deprecated and uses should be replaced with Robolectric.buildAttributeSet(). -* RobolectricGradleTestRunner is deprecated and uses should be replaced with RobolectricTestRunner. +* `RoboAttributeSet` is deprecated and uses should be replaced with [`Robolectric.buildAttributeSet()`](javadoc/latest/org/robolectric/Robolectric.html#buildAttributeSet()). +* `RobolectricGradleTestRunner` is deprecated and uses should be replaced with [`RobolectricTestRunner`](javadoc/latest/org/robolectric/RobolectricTestRunner.html). --- @@ -262,71 +330,119 @@ Robolectric.setupContentProvider(MyContentProvider.class); ### New Features * Support for API 19 (KitKat) - * Support for API 21 (Lollipop) - * Custom test runner for Gradle / Android Studio: + +=== "Java" -``` -@RunWith(RobolectricGradleTestRunner.class) -@Config(constants = BuildConfig.class) -``` + ```java + @RunWith(RobolectricGradleTestRunner.class) + @Config(constants = BuildConfig.class) + ``` + +=== "Kotlin" + + ```kotlin + @RunWith(RobolectricGradleTestRunner::class) + @Config(constants = BuildConfig::class) + ``` ### Major Changes -| 2.4 | 3.0 | -| ------------- | ------------- | -| `Robolectric.application` | `RuntimeEnvironment.application` | -| `Robolectric.shadowOf` | `Shadows.shadowOf` | -| `Robolectric.Reflection.setFinalStaticField` | `ReflectionHelpers.setStaticField` | -| `org.robolectric.Robolectric.Looper` | `org.robolectric.shadows.ShadowLooper.Looper` | -| `org.robolectric.Robolectric.getBackgroundScheduler` | `org.robolectric.Robolectric.getBackgroundThreadScheduler` | -| `org.robolectric.Robolectric.runBackgroundTasks` | `org.robolectric.Robolectric.getBackgroundThreadScheduler().advanceBy(0)` (also `org.robolectric.Robolectric.flushBackgroundThreadScheduler` will run delayed background tasks too) | -| `org.robolectric.Robolectric.getUiThreadScheduler` | `org.robolectric.Robolectric.getForegroundThreadScheduler` | -| `org.robolectric.Robolectric.runUiThreadTasks` | `org.robolectric.shadows.ShadowLooper.runUiThreadTasks` | -| `org.robolectric.Robolectric.runUiThreadTasksIncludingDelayedTasks` | `org.robolectric.shadows.ShadowLooper.runUiThreadTasksIncludingDelayedTasks`
or
`org.robolectric.Robolectric.flushForegroundThreadScheduler` | -| `org.robolectric.Robolectric.getShadowApplication` | `org.robolectric.shadows.ShadowApplication.getInstance` | -| `FragmentTestUtil.startFragment`(v4/v11) | `SupportFragmentTestUtil.startFragment` (v4)
`FragmentTestUtil.startFragment` (v11) | -| `org.robolectric.tester.android.view.TestMenuItem` | `org.robolectric.fakes.RoboMenuItem` | -| `ActivityController.of` | `Robolectric.buildActivity` | -| `org.robolectric.Config.properties` | `robolectric.properties` | -| `@Config(emulateSdk=...)` / `@Config(reportSdk=...)` | `@Config(sdk=...)` | -| `org.robolectric.shadows.ShadowHandler` | `org.robolectric.shadows.ShadowLooper` | -| `org.robolectric.shadows.ShadowSettings.SettingsImpl` | `org.robolectric.shadows.ShadowSettings.ShadowSystem` | -| `Robolectric.packageManager = instance` | `RuntimeEnvironment.setRobolectricPackageManager(instance)` | -| `org.robolectric.res.builder.RobolectricPackageManager`
changed from `class` to `interface` | `org.robolectric.res.builder.DefaultPackageManager` | -| `org.robolectric.shadows.ShadowMenuInflater` | ? | -| `org.robolectric.Robolectric.clickOn` | `org.robolectric.shadows.ShadowView.clickOn` | +| 2.4 | 3.0 | +|------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Robolectric.application` | `RuntimeEnvironment.application` | +| `Robolectric.shadowOf` | `Shadows.shadowOf` | +| `Robolectric.Reflection.setFinalStaticField` | `ReflectionHelpers.setStaticField` | +| `org.robolectric.Robolectric.Looper` | `org.robolectric.shadows.ShadowLooper.Looper` | +| `org.robolectric.Robolectric.getBackgroundScheduler` | `org.robolectric.Robolectric.getBackgroundThreadScheduler` | +| `org.robolectric.Robolectric.runBackgroundTasks` | `org.robolectric.Robolectric.getBackgroundThreadScheduler().advanceBy(0)` (also `org.robolectric.Robolectric.flushBackgroundThreadScheduler` will run delayed background tasks too) | +| `org.robolectric.Robolectric.getUiThreadScheduler` | `org.robolectric.Robolectric.getForegroundThreadScheduler` | +| `org.robolectric.Robolectric.runUiThreadTasks` | `org.robolectric.shadows.ShadowLooper.runUiThreadTasks` | +| `org.robolectric.Robolectric.runUiThreadTasksIncludingDelayedTasks` | `org.robolectric.shadows.ShadowLooper.runUiThreadTasksIncludingDelayedTasks`
or
`org.robolectric.Robolectric.flushForegroundThreadScheduler` | +| `org.robolectric.Robolectric.getShadowApplication` | `org.robolectric.shadows.ShadowApplication.getInstance` | +| `FragmentTestUtil.startFragment`(v4/v11) | `SupportFragmentTestUtil.startFragment` (v4)
`FragmentTestUtil.startFragment` (v11) | +| `org.robolectric.tester.android.view.TestMenuItem` | `org.robolectric.fakes.RoboMenuItem` | +| `ActivityController.of` | `Robolectric.buildActivity` | +| `org.robolectric.Config.properties` | `robolectric.properties` | +| `@Config(emulateSdk=...)` / `@Config(reportSdk=...)` | `@Config(sdk=...)` | +| `org.robolectric.shadows.ShadowHandler` | `org.robolectric.shadows.ShadowLooper` | +| `org.robolectric.shadows.ShadowSettings.SettingsImpl` | `org.robolectric.shadows.ShadowSettings.ShadowSystem` | +| `Robolectric.packageManager = instance` | `RuntimeEnvironment.setRobolectricPackageManager(instance)` | +| `org.robolectric.res.builder.RobolectricPackageManager`
changed from `class` to `interface` | `org.robolectric.res.builder.DefaultPackageManager` | +| `org.robolectric.shadows.ShadowMenuInflater` | ? | +| `org.robolectric.Robolectric.clickOn` | `org.robolectric.shadows.ShadowView.clickOn` | * `Robolectric.shadowOf_` has been removed. Similar functionality exists in `ShadowExtractor.extract`. ### Modules -Note: Shadows for non-core Android classes has moved out of the main Robolectric module. If you want to use those shadows, you'll need to include the requisite module. +> [!NOTE] +> Shadows for non-core Android classes have moved out of the main Robolectric module. If you want to use those shadows, you'll need to include the requisite module. -**`org.robolectric:robolectric:3.0`** +#### `org.robolectric:robolectric` Main "core" module for Robolectric 3.0. +=== "Groovy" + + ```groovy testCompile 'org.robolectric:robolectric:3.0' + ``` + +=== "Kotlin" + + ```kotlin + testCompile("org.robolectric:robolectric:3.0") + ``` Some of the shadows in Robolectric have been split out into separate modules to reduce the number of transitive dependencies imposed on projects using Robolectric. If you want to use any of these shadows, -simply add the the artifact below to your build. +add the needed artifacts below to your build. -**`org.robolectric:shadows-support-v4:3.0`** +#### `org.robolectric:shadows-support-v4` + +Shadows for classes in the Android `support-v4` library. -Shadows for classes in the android support-v4 library. +=== "Groovy" + ```groovy testCompile 'org.robolectric:shadows-support-v4:3.0' + ``` + +=== "Kotlin" -**`org.robolectric:shadows-httpclient:3.0`** + ```kotlin + testCompile("org.robolectric:shadows-support-v4:3.0") + ``` + +#### `org.robolectric:shadows-httpclient` -Shadows for classes in Apache HTTP client. This includes methods like `Robolectric.getLatestSentHttpRequest`. These methods have moved to `FakeHttp.getLatestSentHttpRequest`. +Shadows for classes in Apache HTTP client. This includes methods like `Robolectric.getLatestSentHttpRequest`. These methods have moved to [`FakeHttp.getLatestSentHttpRequest`](javadoc/latest/org/robolectric/shadows/httpclient/FakeHttp.html#getLatestSentHttpRequest()). + +=== "Groovy" + ```groovy testCompile 'org.robolectric:shadows-httpclient:3.0' + ``` + +=== "Kotlin" + + ```kotlin + testCompile("org.robolectric:shadows-httpclient:3.0") + ``` -**`org.robolectric:shadows-maps:3.0`** +#### `org.robolectric:shadows-maps` -Shadows for classes in Google maps. +Shadows for classes in Google Maps. +=== "Groovy" + + ```groovy testCompile 'org.robolectric:shadows-maps:3.0' + ``` + +=== "Kotlin" + + ```kotlin + testCompile("org.robolectric:shadows-maps:3.0") + ```