1[< Instrumentation](/testing/android/docs/instrumentation.md) 2 3# Creating a component for testing 4 5Creating a component, e.g. a [ContentProvider][1] or [Service][2], for use 6in testing can be tricky. This is particularly true when: 7 - app code and test code are in separate APKs 8 - the test APK instruments the app APK 9 - the test Service depends on app code. 10 11This doc explains the pitfalls in creating a test component and how to 12avoid them. 13 14[TOC] 15 16## What can go wrong with a test component 17 18> **tl;dr:** test components may not be able to access app code when defined in 19> the test APK. 20 21Chromium's instrumentation test suites are all currently set up in the manner 22described above: app code is in one APK (the _APK under test_), test code is 23in another APK (the _test APK_), and auxiliary code, when necessary, is in 24one or more other APKs (_support APKs_). Test APKs build against app code 25but do not retain it in their .dex files. This reduces the size of test APKs 26and avoids potentially conflicting definitions. At instrumentation runtime, 27the test code is loaded into the app package's process, and it's consequently 28able to access code defined in the app APK's .dex file(s). Test components, 29however, run in the test package's process and only have access to code in the 30test APK. While test components referencing app code will build without issue, 31they will fail to link at runtime and will consequently not be able to be 32instantiated. 33 34For example, here's the logcat from an attempt to use a test Service, 35`TestPostMessageService`, that extended an app Service, `PostMessageService`. 36Note that the runtime link failed because the superclass couldn't be resolved, 37and the test Service could not be instantiated as a result. 38 39``` text 40Unable to resolve superclass of Lorg/chromium/chrome/browser/customtabs/TestPostMessageService; (184) 41Link of class 'Lorg/chromium/chrome/browser/customtabs/TestPostMessageService;' failed 42... 43FATAL EXCEPTION: main 44Process: org.chromium.chrome.tests, PID: 30023 45java.lang.RuntimeException: 46 Unable to instantiate service org.chromium.chrome.browser.customtabs.TestPostMessageService: 47 java.lang.ClassNotFoundException: 48 Didn't find class "org.chromium.chrome.browser.customtabs.TestPostMessageService" on path: 49 DexPathList[ 50 [zip file "/system/framework/android.test.runner.jar", 51 zip file "/data/app/org.chromium.chrome.tests-1.apk"], 52 nativeLibraryDirectories=[ 53 /data/app-lib/org.chromium.chrome.tests-1, 54 /vendor/lib, 55 /system/lib]] 56 at android.app.ActivityThread.handleCreateService(ActivityThread.java:2543) 57 at android.app.ActivityThread.access$1800(ActivityThread.java:135) 58 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278) 59 at android.os.Handler.dispatchMessage(Handler.java:102) 60 at android.os.Looper.loop(Looper.java:136) 61 at android.app.ActivityThread.main(ActivityThread.java:5001) 62 at java.lang.reflect.Method.invokeNative(Native Method) 63 at java.lang.reflect.Method.invoke(Method.java:515) 64 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) 65 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) 66 at dalvik.system.NativeStart.main(Native Method) 67Caused by: java.lang.ClassNotFoundException: 68 Didn't find class "org.chromium.chrome.browser.customtabs.TestPostMessageService" on path: 69 DexPathList[ 70 [zip file "/system/framework/android.test.runner.jar", 71 zip file "/data/app/org.chromium.chrome.tests-1.apk"], 72 nativeLibraryDirectories=[ 73 /data/app-lib/org.chromium.chrome.tests-1, 74 /vendor/lib, 75 /system/lib]] 76 at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) 77 at java.lang.ClassLoader.loadClass(ClassLoader.java:497) 78 at java.lang.ClassLoader.loadClass(ClassLoader.java:457) 79 at android.app.ActivityThread.handleCreateService(ActivityThread.java:2540) 80 ... 10 more 81``` 82 83## How to implement a test component 84 85There are (at least) two mechanisms for avoiding the failures described above: 86using a support APK or using sharedUserIds. 87 88### Use a support APK 89 90Putting the Service in a support APK lets the build system include all necessary 91code in the .dex without fear of conflicting definitions, as nothing in the 92support APK runs in the same package or process as the test or app code. 93 94To do this: 95 96**Create the component.** 97 98It should either be in an existing directory used by code in the appropriate 99support APK or a new directory for such purpose. In particular, it should be 100in neither a directory containing app code nor a directory containing test 101code. 102 103``` java 104package org.chromium.chrome.test; 105 106import org.chromium.chrome.MyAppService; 107 108public class MyTestService extends MyAppService { 109 ... 110} 111``` 112 113**Put the component in a separate gn target.** 114 115This can either be a target upon which the support APK already depends or a new 116target. 117 118``` python 119android_library("my_test_service") { 120 sources = [ "src/org/chromium/chrome/test/MyTestService.java" ] 121 deps = [ ... ] 122} 123``` 124 125The support APK must depend on this target. The target containing your test 126code can also depend on this target if you want to refer to the component 127class directly, e.g., when binding a Service. 128 129> **NOTE:** Even if your test code directly depends on the service target, 130> you won't be able to directly reference the test component instance from 131> test code. Checking a test component's internal state will require adding 132> APIs specifically for that purpose. 133 134**Define the component in the support APK manifest.** 135 136``` xml 137<manifest ... 138 package="org.chromium.chrome.tests.support" > 139 140 <application> 141 <service android:name="org.chromium.chrome.test.MyTestService" 142 android:exported="true" 143 tools:ignore="ExportedService"> 144 ... 145 </service> 146 147 ... 148 </application> 149</manifest> 150``` 151 152### Use a sharedUserId 153 154Using a [sharedUserId][3] will allow your component to run in your app's 155process. 156 157> Because this requires modifying the app manifest, it is not recommended at 158> this time and is intentionally not further documented here. 159 160[1]: https://developer.android.com/reference/android/content/ContentProvider.html 161[2]: https://developer.android.com/reference/android/app/Service.html 162[3]: https://developer.android.com/guide/topics/manifest/manifest-element.html#uid 163