What's the difference between getTargetContext() and getContext (on InstrumentationRegistry)?

InstrumentationRegistry is an exposed registry instance that holds a reference to the instrumentation running in the process and it's arguments and allows injection of the following instances:

  • InstrumentationRegistry.getInstrumentation(), returns the Instrumentation currently running.
  • InstrumentationRegistry.getContext(), returns the Context of this Instrumentation’s package.
  • InstrumentationRegistry.getTargetContext(), returns the application Context of the target application.
  • InstrumentationRegistry.getArguments(), returns a copy of arguments Bundle that was passed to this Instrumentation. This is useful when you want to access the command line arguments passed to Instrumentation for your test.

EDIT:

So when to use getContext() vs getTargetContext()?

The documentation doesn't do a great job of explaining the differences so here it is from my POV:

You know that when you do instrumentation tests on Android then you have two apps:

  1. The test app, that executes your test logic and tests your "real" app
  2. The "real" app (that your users will see)

So when you are writing your tests and you want to load a resource of your real app, use getTargetContext().

If you want to use a resource of your test app (e.g. a test input for one of your tests) then call getContext().


You may need InstrumentationRegistry.getContext() for access to raw resources of a test case.

E.g., to access app/src/androidTest/res/raw/resource_name.json in a TestCase:

final Context context = InstrumentationRegistry.getContext(); InputStream is = context.getResources().openRawResource(com.example.package.test.R.raw.resource_name);


Took me hours to find that out.

A InstrumentedTest case had a context member which was set up in the setup like this:

context = InstrumentationRegistry.getTargetContext();

this was used to open files, especially things like that:

String filenameOriginal = context.getCacheDir() + "/initial.csv";

Now I decided that I need to use some resource which is only available in the instrumented test, I did not want to distribute this resource with the release version. Therefore I recreated the resource directories under test:

app\src\androidTest
└───res
    └───raw
            v1.csv

But to be able to use this in code I had to call something like this:

    public static Uri resourceToUri(Context context, int resID)
    {
        return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
                context.getResources().getResourcePackageName(resID) + '/' +
                context.getResources().getResourceTypeName(resID) + '/' +
                context.getResources().getResourceEntryName(resID));
    }

    resourceToUri(context, R.raw.v1)

Which would always fail because R.raw.v1 coincidently corresponded to something that was actually in my R resource file from the main application. By using resources in the instrumented tests, there were two R files generated. To fix that I had to include the test R file:

import com.my_thing.app.my_app.test.R;

the resourceToUri call would then however sill fail.

The issue was, that I must not have used InstrumentationRegistry.getTargetContext() but ratherInstrumentationRegistry.getInstrumentation().getContext() to get the resource of R.raw.v1 so I blindly replace the setup of the context for the whole test class to

context = InstrumentationRegistry.getInstrumentation().getContext();

It worked well for the specific test but other tests in the test case started to fail with permission denied for the filenameOriginal I was using above.

Turned out that by replacing the context to the instrumentation context I obtained a path to which my app had no access and I got FileNotFoundException with permission denied and no GrantTestRule or other things would work.

So be careful when using those contexts, it might screw your time :/