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

16,969

Solution 1

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().

Solution 2

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 :/

Solution 3

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);

Share:
16,969
Zsolt Safrany
Author by

Zsolt Safrany

Don't comment - rather refactor. ;)

Updated on June 06, 2022

Comments

  • Zsolt Safrany
    Zsolt Safrany about 2 years

    I'm using the new Android Testing Support Library (com.android.support.test:runner:0.2) to run Instrumentation Tests (a.k.a Device or Emulator Tests).

    I annotate my test class with @RunWith(AndroidJUnit4.class) and use Android Studio to run them.

    For my test cases I need a Context instance. I can get it with InstrumentationRegistry but it has two context related methods and it's not clear what the difference is.

    What is the difference between InstrumentationRegistry.getContext() vs. InstrumentationRegistry.getTargetContext()?

  • IgorGanapolsky
    IgorGanapolsky over 7 years
    So when to use getContext() vs getTargetContext()?
  • cidermole
    cidermole about 7 years
    I just had to use getContext() because I was getting the wrong file from openRawResource(). I was trying to access a raw resource in the test package, app/src/androidTest/res/raw, but kept getting the wrong file when using getTargetContext().getResources().openRawResource(com.exampl‌​e.test.R.raw.resourc‌​e_name. Solution: getContext().getResources().openRawResource(com.example.test‌​.R.raw.resource_name gave me the correct file.
  • kurdtpage
    kurdtpage about 6 years
    The InstrumentationRegistry class does not exist
  • Samuel
    Samuel about 6 years
    @kurdtpage did you import it via import android.support.test.InstrumentationRegistry;