How can I unit test Android resources in feature modules? - android

My app is using feature modules. One of the feature modules declares a bunch of strings in an xml resource, and then accesses them from my kotlin code: e.g. getString(R.string.dialog_title). I want to run Junit tests to prove that the correct string content is retrieved.
The Robolectric documentation page suggests using the function getApplicationContext<MyApplication>(). However, you don't have access to the Application when unit testing a feature module. I guess that's because feature modules exist separately to the application?
Any help appreciated. :)

Robolectric will slow your unit tests a bit. You can just mock context object and then do something like: when(context.getString(any()).thenReturn('any/empty string')

Related

What type of things should be tested in androidTest versus test

I am beginning to learn android development and I have a quick question about what should be tested where.
I have been looking through and studying the sample repository from android/architecure-samples . I was wondering based on this type of project what gets tested from where?
For example what I gathered from the repository above:
androidTest
Activity
Navigation
unit
Repository
ViewModel
sharedTest
Fragment
Dao
DataSource
Can someone shed some light on if this is correct or a common pattern? To me this seems rather odd. I would think that Fragment would be in androidTest and Dao and DataSource would be in unit test?
androidTest contains tests that required hardware to be involved. You can access the Context of your app. It is usually used for integration or UI automation test
test contains tests that can run on local JVM. You can write tests for utils methods, funtional of classes
Refer: https://developer.android.com/training/testing/unit-testing
sharedTest contains tests of 2 types above. Why we have this? Basically, sometimes you should have test data that is used for both local and instrument tests. Instead of duplicate the data to test and androidTest folder, you can setup it here then write your tests.
Example: FakeRepository is declared in sharedTest. It can be used in test
Refer: https://proandroiddev.com/sharing-code-between-local-and-instrumentation-tests-c0b57ebd3200

How to initialize emulated external dependencies in a Kotlin Android UI test

I'll be developing an Android app in Kotlin and I'm trying to figure out how to initialize emulated dependencies. For example, the app will make API calls to a server, obtain the user's location from a location provider, pull down images from a content management system, store data locally in a database and in Android's Shared Preferences, and do math based on the current date/time. Thus there are a lot of external dependencies I want to emulate, including the current date/time so I can verify age calculation, etc.
My goal for testing is just to validate my app's screens using an Android instrumented test. I don't want any dependency on real external systems because testing those systems is the responsibility of the developers of those systems.
While reading Android'd documentation Consider whether to use test doubles, I noticed it offered a good tip: "Tip: Check with the library authors to see if they provide any officially-supported testing infrastructures, such as fakes, that you can reliably depend on." However, the documentation doesn't really explain how to initialize a 3rd party test infrastructure.
Below are what I understand so far about my options, but they all come back to a fundamental thing I don't understand: how does an Android app under test know it should operate in a test mode vs. a production mode?
Mocking such as Mockito or MockK: Mocking seems like a special case of Dependency Injection tailored for testing. The examples I've seen involve testing a class or a method, not a full scale system. The examples typically show how you mock a class and pass it to the class/method under test. But with a full scale system, the test code operates on widgets referenced via Espresso. There is no access to the classes where the logic is. My impression is mocking is for unit testing, not UI testing. But maybe someone can explain how to use mocking for UI testing:
a) Suppose an external dependency is initialized deep in the call stack. If I define a mock in my test code's setup function (e.g. a method annotated with #Before), how do I pass that down to the place in the code that depends on it?
b) I keep reading that mocks don't work in Kotlin because Kotlin defines all classes as final. There seem to be a few work arounds. But does does Google/Android officially recommend one of these (I haven't read it in their documentation).
Dependency Injection such as Dagger 2: If mocking isn't viable for UI testing, then should I use Dependency Injection? From what I understand, it seems Dagger 2 deals with issue 1.a above by defining a top-level Component and a tree of Modules which can provide dependencies at any layer of the stack. For testing, it seems like I would just provide a different Component that emulates the real dependencies.
a) In an Android instrumented test, where do instantiate a Dagger 2 Component designed for testing. How do I make sure that Component is used rather than the Component intended for production?
Prepare before launching test: I can see how I could customize build.gradle to prepare my test environment before my application is launched. For example, I could pass a flag to my app so that when the Application's onCreate() gets called, I can configure my system to prepare emulated dependencies via Dependency Injection, Mocking, or even just a custom implementation. For example, some external dependencies have a test mode where I would need to pass a flag to them so they work in test mode. I'm not clear how that sort of thing reconciles with Dependency Injection or Mock but I guess I could see how I could use those mechanisms as a wrapper to pass the test flag or not. In the following post someone wanted to mock a location provider and to do that they modified their build.gradle file to set things up before the Android test infrastructure started.
How to set Allow Mock Location on Android Device before executing AndroidTest with uiautomator and espresso?
In conclusion, I want to test a Kotlin Android app's UI using Android instrumented test with Espresso but I don't know how to setup the test so that external dependencies use emulation code rather than production code. Should I use mocking, Dependency Injection, or customize via build.gradle? Can someone help me get my thinking on track?
After much searching, I've discovered that the Android ActivityTestRule allows you to defer launching the Activity. This gives the test code time to initialize emulated dependencies as demonstrated in Fast Mocked UI Tests on Android Kotlin.

A way to share code between multiple android test modules

I want to achieve something like this:
[ComponentTestsModule] com.android.test
[FunctionalTestsModule] com.android.test
both depends on
-> [TestLibraryModule] ?
which depends on
-> [AppModule] com.android.application
Is there any way to do it with android Gradle plugin 3.0+?
Why I need multiple test modules?
I want different test runners for different types of tests, also target different variants.
It is working right now with single codebase under androidTest, but with ugly switches in the custom test runner.
Why I need a test library module?
I want to share the same page-objects between different types of tests, and maybe some utility code.
Problem is: that page objects must have access to R class of app (locators: R.id.*)
None of the module types I'm aware of can depend on APK-producing module, expect from com.android.test, but i cannot depend from com.android.test with another com.android.test.
I have recently faced this problem and just want to share my solution in case some body needs it.
What I have done so far (not a perfect solution - I guess, but at least it works).
Create a new module named testing_base or some thing like that
In testing_base module only put things related to testing (Like the code you want it to be shared between modules) in to normal packages, NOT in the test packages/folders.
From others module, try to import things from testing_base module
ex: testImplementation project(":testing_base")
Hope it can helps someone, happy testing!
I want different test runners for different types of tests
To run a test class with a specific test runner, use the #RunWith annotation.
also target different variants
To target a specific variant, create your tests in app/src/androidTestVariantName for instrumented tests or in app/src/testVariantName for unit tests.

dynamic binding with dagger 2

I have problems migrating my Android app from Dagger to Dagger2.
I use Dagger to inject mocks at different layers during testing (unit , functional device and everything in between).
E.g. I have a NetworkingModule which provides a HttpTransport and a ServerConnector. For some test I just mock the http layer, for others I mock the ServerConnector, and other test use release classes to test integration with a real test server.
These test can be run on all build flavors (debug, release, ...).
I found only descriptions of Dagger2 which set a single static injection configuration per build type - i.e. a release component to bind real classes and a debug component to bind one set of mocks.
I need to find a way to have the test cases replace modules in the application start up phase.
How can I achieve this? In my thoughts I always get stuck when the application triggers the building of the graph for which it has to use a Component which is the implementation of a certain interface which has static annotation of its modules???
To ask differently: Is it possible to compile multiple modules in the same build which provide the same injections? How would I then inject with the right module at run time?
Can someone please draft me a solution for my example of the NetworkingModule.
BTW: I make heavy use of Espresso, Robolectric, and Mockito.
Thx.
Found a solution:
I don't need build specific Components or Modules.
My modules just handle delegates which I implement and set for injecting mocks when testing. This is very cheap for release builds. And for testing I can even effort some reflection for convenience features.

Using JUnit on android project by mocking few android api classes?

I have some sourcecode in my android project that just uses plain java code.
I created UnitTests (JUnit) for this classes to test them.
The problem is that the functions use internally some android classes yet.
These are Log and Base64 at the moment.
Now I am looking for a simple way to mock this Classes but I was not able to do so.
I googled and found a lot of mocking tools like jmocking, mockito, robolectric and so on, but I am very confused with this libraries and did not get one of them to work.
I did not find one good step by step tutorial that shows me how to simply mock some android class so I do not get this stub / classnotfound errors.
Questions would be:
What is the most common used android mocking library?
How do I mock some Class that I do not use directly in my UnitTest, but that are used by the functions I call?
Thanks,
Subby
I'm not sure that any mocking library will fully support Android. There are several technical reasons why Android and mocks don't get along.
If you want to unit test a java object that uses the Android API, you usually have to use one of the test case classes in android.test.*. Android classes often need an Android context in order to work correctly.
For a unit test, you usually inject a dependency, but there are limits. After all, you don't unit test the multiplication operation! You can unit test the Android classes, and once you're satisfied they're working, you can assume that they're working correctly.
You'll need robolectric to mock Android classes: http://pivotal.github.com/robolectric/index.html

Categories

Resources