Injecting dependencies in multiprocess Android applications - android

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"

Related

Use ContentProvider to run code on app startup

I'm making an android library and I want to run some initialization code when the application starts.
In some cases, it might be a little difficult for the developer(using my library) to implement a custom application class. AFAIK, the ContenProvider codes start before application starts and does not need any custom implementation done by the developer using my library.
The question is, Can I use ContentProvider to run code at Application's start up?
I simply create a provider class:
class ExampleProvider : ContentProvider() {
override fun onCreate(): Boolean {
// run code at startup and do initialization
return true
}
...
// Ignore other methods and return null or 0
}
And add the manifest tag.
And it will run before the application starts up. (Without the need of Custom Application class) But will it always behave like this and run code before app starts? Is there any case that ContentProviders will not launch at app startup?
TL; DR
How an android library can run code at application startup, without asking developers to implement a custom Application class?
Recently Android has provided a startup library to initialize components at application startup. If you see the source code of that library, you will see they use ContentProvider to start the library at startup.

How to write an android test which will verify my app hits all required apis

I am heavily testing my application with unit tests and Espresso tests. My next step is to make sure my application hits all required apis. For that reason I am looking for a way to write a test, which will verify the api calls.
I would really appreciate any suggestions.
What you are describing is called a "unit test". Unit tests are meant to test as many lines of code as possible regardless of UI.
Espresso tests are "instrumentation tests" (or "UI tests") intended to check if the app is responding to UI events correctly. They're not meant to verify the correctness of code, but the correctness of the functionality of the app as used by the user.
You can read about both at the official documentation. You'll find that unit tests are very different than instrumentation tests, and often harder to write because they require more engineering of your code to do correctly. You will likely have to "mock" the various parts of your application to make sure their APIs were called exactly as you expected.
There are 2 main goal when I was writing api tests:
First is component based. The goal was to make sure each class / component makes an api call when certain criteria is met (for example calling an api A when onDestroy() is called)
Second, is to make sure the Apis are called is certain order for the analytics purposes.
The first step I achieved by using unit tests with injected mock objects via Mockito and PowerMockito. PowerMockito was used primarily to mock static methods and to make sure the methods were called at least n times.
For the second step, UI test could be used, since it runs the real application. I have implemented the helper class, which was recording the instances when api requests were made. The script in Espresso was validating the order of api calls by referring the helper class.

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.

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

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

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