After a recent migration to Dagger 2, the app I am working on is using an #ActivityScope for every feature. Each app feature is implemented using MVP pattern and has it's own local dagger Component setup which depends on the Application component for the dependencies that are required during the entire app lifecycle (provided by the App). Each feature’s Activity extends a base class which provides the main application component to a method that is overridden by each activity in order to set up the local dagger component (builds the local component and instantiates the local module).
The issue I am trying to solve is how to inject mocks into the Activity under test. The main problem I am experiencing is that I cannot swap the original local component and corresponding module with mocked ones at runtime. There are many articles on Espresso testing with Dagger 2 that I read, but they are not promoting clean architecture. For the most part they rely on the AppComponent to inject all Activities, where in my case, each feature’s component is responsible for injecting it’s own activity.
So far the best approach that I came up with was to introduce a component builder that is only initialised as part of the test setup and in the Activity code to go with this setup if initialised, otherwise set up the real component. However, I am reluctant to mix production and test code.
Here is a schematic which represents the Dagger setup:
Dagger 2 Setup
The approach that I took at the end was to create a custom AndroidJUnitRunner as described here and to create Components/Modules which provide mocks for each app feature, including the Application module check Google doc for best practices. Furthermore, each activity under test has to be overridden to inject the mocks (just the method that injects the mocked dependencies).
In order to keep the main app manifest clean, the overridden test activities are declared in a debug manifest.
Hope this approach helps people with a similar to my Dagger 2 setup to do their testing with Espresso and Dagger 2.
Have a rock ’n’ roll day!
Related
Square Inc. has presented it's internal modular architecture at Droidcon SF'19:
https://www.droidcon.com/media-detail?video=380843878
However, I'm a bit confused with some bullets. Could you please help me?
Why do they actually need :wiring modules? I find it adding complexity:
you get extra gradle module for each new feature
you have to make a sort of global injection into your Fragments somewhere in :app, because Fragments defined in :impl modules cannot access it's DaggerComponent, which is defined in :impl-wiring modules. :impl doesn't depend on :impl-wiring, because the dependency is reversed.
you cannot have an Android Dynamic Feature modules, because they should know about it's DaggerComponent in order to inject it's Fragment. But there is no way to do such injection from :app module, which is base-module for Dynamic Features.
so why :wiring modules at all?
One can merge :impl and :impl-wiring, or :fake and :fake-wiring together to eliminate all the issues mentioned above. And also, in :demo-apps one could just have a dependency on either :impl or :fake``, and not on :impl-wiring(or:fake-wiring```).
The creation of this type of modules is to separate even more. With this you generate an abstraction of the type of component you use (koin, dagger) and how. If the project is large, it makes sense to do it.
Currently I generate the following flow of dependencies between modules:
WARNING: Check the directionalities well.
:feature-A:open <- :feature-A:impl -> :feature-A:impl-wiring
:feature-A:impl-wiring -> :feature-A:impl, :feature-A:open
:app -> :feature-A:open, :feature-A:impl-wiring
I'm still not sure if app should depend on open and impl-wiring, or which app should only depend on open and open from impl-wiring.
Eventually, I came up with the following solution:
each feature consists of the following gradle-modules:
api
impl and fake
data:api
data:impl1 ... data:implN and data:fake
data:wiring
ui
demo
So, here api, impl and fake as usual, but I've my data layers separated. I bought myself that I need multiple different implementation of data layers sometimes, for example - if I develop Stock-Charts App, I could rely on Finnhub Open API or MBOUM API or provide fake implementation.
Thus I have data:api, data:implX. Indeed, data:api defines FeatureRepository interface (one or many) and data:implX provides actual implementation for them. In order to bind interface and implementation, I use data:wiring, which defines Dagger modules and component(s). In addition, I keep the same package names within each data:implX module in order to "write-once" the data:wiring module. And to replace one implementation with another, I just change a single line in data:wiring/build.gradle which states a sort of:
implementation project(":data:implA")
to
implementation project(":data:implB")
Also, to break the confusion mentioned in my original question, I introduce ui module, which contains some Views of a particular feature. Fragments go in demo (a standalone app to test feature) or in ui, they refer to viewModel which have some bindings ctor-injected from Dagger component of a feature. But the UI and library are separated here. Fragment instantiates a dedicated Dagger component that uses component dependencies to refer to feature's library bindings, such as interactor or repository etc.
So, to wrap up - separation between UI and business logic implementation (a "library") for each feature makes it possible to solve the issue. Feature's api declares an entry point to it's functionality as a library, and it's global access via Dagger multibindings from :app. So it can be used further in any :demo, :ui and :dynamic-feature.
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.
I am using dagger 2.11 for an MVP app. Naturally, I want to write unit tests for my application, but I have run into some problems. I have an activity that has a presenter. When testing that activity I want to inject a mock presenter so I can verify that certain methods are called. The presenter is injected via Dagger 2.11 in the activity. My dagger setup is exactly the same as is described in these two articles:
https://medium.com/#iammert/new-android-injector-with-dagger-2-part-1-8baa60152abe
https://medium.com/#iammert/new-android-injector-with-dagger-2-part-2-4af05fd783d0
I use the annotations described in the latter, meaning I use the #ContributesAndroidInjector in a BuildActivity file instead of separate subcomponents.
My question is: How would I go about injecting the mock of the presenter?
Our Application subclass currently holds most of our singletons (REST adapter, HTTP client, caches etc...). I've modified it so that these get injected via dagger 1, with the hope that I could replace them with mocked versions when running functional tests.
I have tried these approaches:
Have a static variable to an optional test module, that is normally null but gets set from tests. But I haven't found a place where I could run code to set that static variable to my test module in my functional tests (with either ActivityInstrumentationTestCase2 or the new ActivityTestRule). The Application is created before any of my test code runs.
Define the list of modules to use in a class that gets overridden entirely in the test code, as suggested by Jake Wharton in example. But this doesn't seem to work for tests, which seem to include all the classes from the debug variant and all the classes from the androidTest variant, resulting in a conflict. Also it would mean that all my tests get the mocked versions, which might not be ideal.
Is there a better way to inject dependencies from test modules in the Application class, when running functional activity tests?
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.