Goal
To have a custom RunListener
do custom things on test failure when running Android instrumentation tests using Espresso.
tl;dr
InstrumentationInfo.metaData
is null
, even while ApplicationInfo.metaData
has my info. Why?
Progress thus far
I can get my RunListener to work with the following adb command:
adb shell am instrument -w -e listener com.myproject.test.runlisteners.CustomRunListener -e class com.myproject.test.ui.HomeActivityTest#testWillFail com.myproject.test/android.support.test.runner.AndroidJUnitRunner
Which is specified in the documentation for AndroidJUnitRunner here.
However, that documentation also states that one can specify a RunListener in an AndroidManifest.xml
metadata element. I have thus far been unsuccessful getting this to work.
AndroidManifest.xml
I added the following to my <application>
element in main/AndroidManifest.xml
:
<meta-data
android:name="listener"
android:value="com.myproject.test.runlisteners.CustomRunListener" />
This didn't have any effect. By various means, I have discovered that these lines of code (which are used by AndroidJUnitRunner
and RunnerArgs
to get custom metadata arguments from the manifest)
InstrumentationInfo instrInfo = pm.getInstrumentationInfo(
getComponentName(), PackageManager.GET_META_DATA);
Bundle b = instrInfo.metaData;
...return me a null
Bundle.
I noticed that the generated debug/AndroidManifest.xml
didn't have my metadata tag, and so, as an experiment, I also added it to my androidTest/AndroidManifest.xml
file. That looks like this:
<application
android:name=".BaseApplication">
<meta-data
android:name="listener"
android:value="com.sirius.test.runlisteners.CustomRunListener" />
</application>
...which then appears in the generated debug/AndroidManifest.xml
like so:
<application android:name="com.myproject.BaseApplication" >
<meta-data
android:name="listener"
android:value="com.sirius.test.runlisteners.CustomRunListener" />
<uses-library android:name="android.test.runner" />
</application>
This also didn't have any effect.
Another experiment
I created a custom test runner called CustomAndroidJUnitRunner
which extends AndroidJUnitRunner just for the purpose of peaking under the hood a bit. If I do this:
ApplicationInfo ai = packageManager.getApplicationInfo(
getComponentName().getPackageName(), PackageManager.GET_META_DATA);
Bundle b = ai.metaData;
Object o = b.get("listener");
Log.d(TAG, "listener=" + o.toString());
...logcat will say:
D/CustomAndroidJUnitRunner: listener=com.myproject.test.runlisteners.CustomRunListener
So, ApplicationInfo.metaData
has it. Why doesn't InstrumentationInfo.metaData
?
Sometimes it's not until you've taken the time to explain everything thoroughly that you finally understand what the problem is. The solution is to add this to the <manifest>
element:
<instrumentation
android:name="com.myproject.test.runner.CustomAndroidJUnitRunner"
android:functionalTest="false"
android:handleProfiling="false"
android:label="Tests for com.myproject"
android:targetPackage="com.myproject">
<meta-data
android:name="listener"
android:value="com.myproject.test.runlisteners.CustomRunListener" />
</instrumentation>
I just copy-pasted the <instrumentation>
element from the generated debug/AndroidManifest.xml
file.
I was a bit thrown off, originally, because both CustomAndroidJUnitRunner
and com.myproject
were highlighted in red in Android Studio. But everything compiles just fine.
I hope this helps someone else!