How can I use a real Application object with Android's ServiceTestCase - android

I'm trying to test a service that requires a real Application object, not the MockApplication that the ServiceTestCase provides.
How can I create a real Application object in my ServiceTestCase?

A service has two inherent dependencies, its Context and its associated Application. The ServiceTestCase framework allows you to inject these dependencies, and thus perform unit tests with controlled dependencies in an isolated environment.
You should not use the real Application in your tests, but if you know what you are doing you can inject it using ServiceTestCase.setApplication().

Related

Injecting dependencies in multiprocess Android applications

TLDR: I am developing an application which runs in multiple processes. For the purposes of UI testing, I want to inject a fake API dependency to make the app running under tests independent of network interactions, however, this doesn't seem to work in the multi-process setting.
I am using the approach described in this post, so I implemented a custom AndroidJUnitRunner which instantiates the application with mock dependencies (let it be MockApplication) instead of the one with real dependencies (let it be RealApplication). It does work and my app queries the fake API interface from the main process.
My app, however, uses multiple processes, e.g. there is a data processing Service which runs in its own process and which only starts on startService invocation from the application code. For some reason, this process runs with the instance of RealApplication, without any mock dependencies.
Is there any way I could make it work? I tried digging into Android code responsible for application instantiation, but haven't really found anything particular useful yet.
P.S. I am using Dagger 2 for DI, but this is probably not really relevant.
The problem is that your custom application class does not override real one in the AndroidManifest.xml.
You're just telling the instrumentation test runner to run your custom application class but then if app starts another process, Android Framework won't even know that it needs to run your custom application class instead of real one.
So, I'd suggest you to override the application class to the custom one in AndroidManifest.xml during the connectedAndroid task execution, as a result your app will use custom class even without hacking the test runner and whenever new processes start.
I struggled on this problem too as I need to mock network calls emitted from a Service started in its own process.
To use a custom Application object (MockApplication) in every process of your app during your tests, a solution is to inject a build variable in your AndroidManifest.xml with the help of manifestPlaceholders.
I defined two product flavors in build.gradle:
productFlavors {
mock {
manifestPlaceholders = [application:".MockApplication"]
}
prod {
manifestPlaceholders = [application:".RealApplication"]
}
}
prod : will set the real Application object (RealApplication) in manifest
mock : will set the mock Application object (MockApplication) to mock network calls
In AndroidManifest.xml, use the variable "application" like this:
<application
android:label="#string/app_name"
android:name="${application}">
Now, when you want to use the MockApplication, just run your instrumentation test with build variant "mockDebug"

How to perform integration test on android (or how to get and inject several components in TestCase)

I have an application which has some services which communicate to each other. All of them work in a single process so they don't need a Messenger. While application runs, my main service connects to other ones and injects services as dependencies. For example ImportService needs ParserService to perform some operations and so ImportService has method setParserService(ParserService service);
It works fine at real operation but I can't run tests for services which need dependencies, because ServiceTestCase.bindService() only knows how to start/bind a service which it actually tests. It can't start/bind other service which I need as a dependency. It compiles when I try to get an other service but crashes while trying to call bindService().
I've googled a lot but now I can see only three ways of solving it:
Make an application more primitive. Use just classes instead of services;
Not to make injections. Get dependencies inside of a service instead. But this way I'll have to wait while dependencies(services) are bound at some places inside of a service. It will be a bad code. I can't use onServiceConnected() because sometimes I may need more than one service as a dependency.
Not to use tests :-)
So, briefly, the question is: I can bind one specific service in a test by using ServiceTestCase. But how can I bind 2, 10 or more services in one test?
ServiceTestCase provides a framework in which you can test Service classes in a controlled environment. It provides basic support for the lifecycle of a [single] Service, and hooks with which you can inject various dependencies and control the environment in which your Service is tested.
If you are performing integration test of various Services you should test them as part of the Activity or Application using them.

Howto run Android app locally with test app specified mock implementation of a service

Using an interface I can have activities interact with a mock object in unit tests.
However, my issue is that my mock service is providing an alternative data set which cannot be browsed by me via the running app (when conceiving a new test) without the app knowing about the mock class.. which it doesn't because it resides in the test application, not the application. Does anyone know how to run the android app in "test mode" .. ie, as you would see it when the unit tests (ActivityInstrumentationTestCase2) are run against the mock service?

Android unit test/test.runner - access to Application object

I'm using the android.test.runner library and the AndroidTestCase etc. to create some unit tests. I'm finding it useful, but one test requires access to the application object. Usually I just get that from the activity context, e.g.
AppState appState = ((AppState) myActivity.getApplicationContext());
However, the unit tests are in a class which extends AndroidTextCase, and as far as I can see there is no getApplicationContext available. There is a getContext, but I'm not clear if that's what I want. What's the best course of action?
Are you testing your AppState object, or trying to test something else which relies on it?
If you are not testing AppState in this instance can you use a tool such as Mockito to mock up an AppState object for the purposes of your test.

Instrumentation class in the Android API

I have question on the Android API. Android API provides a class called "Instrumentation" class. What is the use of this class? Is the Instrumentation class be used only together with Junit for unit testing.
Can Junit framework can be used to test the methods of the Android API without using the Instrumentation class.
Since Junit package has been included in the Android package, I hope we dont need to use install separately for unit testing.
I would appreciate if you could provide me the information as i can't find these clear information anywhere on the Web.
If we use Junit test framework to test the Android API, can we have test results in the UI format rather than test format.?
Thanks a lot. Apprecite your time.
Regards,
Riyas
The Instrumentation class allows the test case to subscribe to various application events (keypresses, etc), and also programmatically control the UI to enable functional testing of your application.
Therefore, you technically do not need the Instrumentation class for Junit testing if all you are doing is unit testing, and not functional testing of the UI. You could just extend TestCase which does not provide any Instrumentation support.
Here is a link with some fairly good descriptions of the various test classes.
Junit is included within Android and will not need to be installed separately.
Instrumentation API is basically used to inject various kinds of events to the UI so that the application can be stress-tested. You can create an arbitrary event like tapping on the screen and inject it using instrumentation API which is basically like a pseudo event generation.
Instrumentation class is a base class for test frameworks, this provides a very specific functionality, It also provides API to invoke test on device/emulator using ActivityManager Interface via ADB. Also, it can gets back the results to the development machines.
By overriding this class you can enjoy all the context and helper classes you need to run your tests.
Below is a sample implementation, I wrote to explain you the functionality.
public class SampleInstrumentation extends Instrumentation {
#Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
Bundle bundle = new Bundle();
bundle.putString("whatisthis","thisiswhat");
finish(0,bundle);
}
}
Manifest:
...
...
</application>
<instrumentation
android:name=".SampleInstrumentation"
android:targetPackage="com.commons"></instrumentation>
</manifest>
Now if you run this command from your development machine.
adb shell am instrument -w -m com.commons/com.commons.SampleInstrumentation
Your instrumentation class onCreate methods get called and you will get some binary encoded response on your console containing data from the bundle you passed to the 'finish' function.
"
whatisthis
thisiswhat
I hope this will make you understand the functionality specific to this base class. For the sake of some motivation, I would like to add that the new android testing framework androidx.test gets compiled with our app (not part of OS and can be modified) directly extends the Instrumentation class (part of Android Framework/OS implementation). Hence you can write your own implementation of a whole new testing framework just by using this class.

Categories

Resources