How to mock after setup dagger-android 2.15 when writing espresso tests? - android

If we just use plain dagger 2. In the application class, we will have a property which holds the AppComponent. Then we can swap it during espresso tests.
But when I setup my project using dagger-android 2.15. Things becomes more implicit if adopt too much Dagger magic. The code is more clean, but makes testing a little bit hard.
This is the application class:
class App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent
.builder()
.create(this)
.build()
}
}
This is the HomeActivity
class HomeActivity : DaggerAppCompatActivity() {
#Inject
lateinit var userPreference: UserPreference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
if (!this.userPreference.memberRegistered) {
goToActivity(EntryActivity::class.java)
}
}
}
Take this code for example. How to mock that injected userPreference.memberRegistered Which could be a HTTP call underneath?

For those who is interested in this, I got a blog with step by step detail for this:
Basically, the idea is:
You still generate instances for injection in #Module
But We’ll create new #Component A only for testing
This #Component will have a method to get that #Module
During tests, we swap the #Component that the application use with our component A
Then things are easy:
Without DaggerMock
In the #Module, instead of return real instance, you just return mockito mock.
With DaggerMock
You declare the type you want to swap and mock it
You can then use the mock.
No need to change the #Module
It inspires by #AutonomousApps 's solution, but the differences are now you don't need to write the #Component, #Module for each test class.

After trying several approaches, this is the only one that worked for me.

I wrote a blog post that explains how to do this just yesterday: https://dev.to/autonomousapps/the-daggerandroid-missing-documentation-33kj
I don't intend to repeat the entire post for this answer (it's hundreds of words and lines of code to properly set up a test harness with Dagger), but to attempt to summarize:
Add a custom application class in the debug source set (I assume it would also work in the androidTest source set, but I have not tried this).
You also need to reference this application in a AndroidManifest.xml in the same source set.
Create a "Test component" in your androidTest class that extends from your production top-level component and build it.
Use that test component to inject your application, which means you've just replaced your entire Dagger dependency graph with a new one you've defined just for the test suite.
Profit.

Related

Dagger bound multiple times : Trying to create a fake implementation

Hi I'm trying to create a fake presenter of my activity I have my module let's call it Activity1module where I have set all of the presenter, use-case, everything and it works perfect, but when trying to create a screen which uses that exact activity with a fake presenter it says I've bound multiple times that presenter.
What I've did is :
#Module
abstract class Activity1Module{
#Binds
abstract fun providePresenter(impl: PresenterImpl) : Activity1Contract.Presenter
.....
}
Then I have created a new module FakeActivity1Module and it's like this :
#Module(includes = [Activity1Module::class])
abstract class FakeActivity1Module {
#Binds
abstract fun bindsFakePresenter(impl: FakePresenterImpl): Activity1Contract.Presenter
.....
}
But looks like it doesn't like this way, is there any way to use the fake one instead of the production one without creating #Named or touching production code?
Dagger doesn't have any capability to have one module override another's bindings directly. FakeActivity1Module and Activity1Module should include similar bindings but neither should be in the other's includes list.
You can, however, extract common bindings to an Activity1CommonModule (named as you'd like) and have both Activity1Module and FakeActivity1Module include that module. That would allow you to avoid repeating yourself as much as possible, at the conceptual cost of some indirection between files.
You can even include that Activity1CommonModule as a nested interface (or abstract or static class) within Activity1Module; you'd still need Activity1Module's includes to contain its own Activity1Module.Activity1CommonModule.class, but you'd have the benefit of centralizing all of Activity1Module's bindings in a single file, plus the benefit of easily seeing through diffs (git diff, p4 diff, etc) when a binding moves in or out of the common set.
Same class cannot be injected 2 times and it produce bound multiple times error. So for the FakeActivity1Module instead of create new injection. Try to override Activity1Module and apply your changes.
class FakeActivity1Module: Activity1Module {
overrides fun providePresenter(): Activity1Contract.Presenter = FakePresenterImpl()
}
And you use FakeActivity1Module into your component for testing and it should work as expected.

Inject into arbitrary Logic class that is not instanciated by Hilt

I'm currently migrating an app from anko-sqlite to room as anko has been discontinued a long time ago. In the process of doing so I introduced a repository layer and thought I would try to introduce dependency injection to get references to the repository instances in all my classes.
Prior to this I used a singleton instance that I just shoved into my application classes companion object and accessed that from everywhere.
I managed to inject my repositories into Fragments, Workmanager and Viewmodels. But I do now struggle a bit to understand how they forsaw we should handle this with arbitrary logic classes.
For instance my workmanager worker calls a class that instantiates a list of "jobs" and those need access to the repository. The worker itself actually bearly even does need access to the repositories as all the work is abstracted away from it.
How can I make something like this work
class Job(val someExtraArgINeed: Int) {
#Inject
lateinit var someRepository: SomeRepository // <= this should be injected when the job is instanciated with the constructor that already exists
}
For Activities and so forth we have special annotations #AndroidEntryPoint that makes this work. However, the documentation makes it unclear as to how we should get our instances from any other class that isn't an Android class. I understand that constructor injection is the recommended thing to use. But I do not think it would work here for me without an even more massive refactor required than this already would be.
If I understand your question correctly you can use hilt entry points like this
class CustomClass(applicationContext: Context) {
#EntryPoint
#InstallIn([/*hilt component that provide SomeRepository*/ApplicationComponent]::class)
interface SomeRepositoryEntryPoint {
fun provideSomeRepository(): SomeRepository
}
private val someRepository by lazy {
EntryPointAccessors.fromApplication(applicationContext, SomeRepositoryEntryPoint::class.java).provideSomeRepository()
}
fun doSomething() {
someRepository.doSomething()
}
}

Dagger 2 - Strategies for reducing number of classes which require annotations

So I am currently in the process of learning dagger 2, and from the tutorials that I've read so far, for a dependency to be injected, the #Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work, or if I don't want other parts of the code to know that dagger exists.
The application structure I have at the moment is:
App Module - where I'd like to put my DI code in (e.g. dagger modules, etc).
Presentation Module - Views/ViewModels etc.
Domain Module - Use Cases etc.
Data Module - Repositories etc.
With pretty much this style of classes contained in my application:
class ExampleViewModelImpl(useCase: ExampleUseCase): ViewModel() in Presentation (gets initialised from an Activity or similar).
class ExampleUseCaseImpl(repository: ExampleRepository): ExampleUseCase in Domain
class ExampleRepositoryImpl(dao: ExampleDao): ExampleRepository in Data
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible? Code examples of how this is achieved would be great.
I am unsure of some terminologies, and wasn't able to find a solution online. If there are good resources which explains what I'm asking, that would also be great.
if I don't want other parts of the code to know that dagger exists.
#Inject is a standard (JSR 330) which Dagger implements. Adding those annotations doesn't have anything to do with Dagger and can be used the same way with other DI frameworks. If it's your code you should just add those #Inject annotations where appropriate. Think of them as documentation: What constructor/field/method must be injected to create & use this object?
The only place where your classes will know that Dagger exists is at the same place where you'd be creating the objects otherwise, too.
Going down that path, of course you could use Dagger without any #Inject annotations, but you'd be writing a lot of unnecessary boilerplate and missing out on the most powerful feature of Dagger at the same time (code generation).
#Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work
That's what #BindsInstance with the #Component.Builder is for (add an object to the component) and what #Provides annotated methods are for (create and initialize an object from a module)
If you really want to write code without #Inject, then you'd do exactly this for all of your objects. This means a lot of modules, and even more #Provides annotated methods. It will work, but I don't see the point in writing all those methods if a single #Inject on the constructor has the same effect.
In my opinion the best thing about Dagger is that I can add / remove / change constructor parameters and don't have to touch any other parts of my code since Dagger will generate new code with the new arguments. In your case you'd have to also change the parameters to the #Provides method as well as the constructor invocation.
Next let's look at how to remove #Inject from fields. Basically you don't want to do field injection, so instead of writing an injection method in the component, you'd write provision methods.
#Component
class MyComponent {
fun inject(activity: MyActivity)
}
class MyActivity {
#Inject lateinit var myDep: Dependency
fun onCreate() {
component.inject(this)
}
}
Removing the #Inject we need to use the provision methods instead.
#Component
class MyComponent {
fun provisionMyDependency() : Dependency
}
class MyActivity {
lateinit var myDep: Dependency
fun onCreate() {
myDep = component.provisionMyDependency()
}
}
It will work and everything, but again, you will miss out on the single best feature of Dagger: Code generation. The example above looks alright because I only added a single dependency, but think about what happens to those 2 different implementations when you add / remove / change dependencies, how well it will scale. If you prefer to do things manually any refactoring will become arduous.
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible?
Your question (especially the title) is in direct conflict with your goal. If you don't want to use those annotations, then you can't use Dagger code generation & injection but have to resort to do it manually as highlighted above.
with as much automated dependency injection as possible
To best utilize Dagger you add #Inject on the constructor and/or fields of every class that should end up on your dependency graph and let Dagger do its thing.

How to inject test overrides into the default dependency graph?

I would like to inject mocked overrides into my Android instrumentation tests using Kodein. I don't know which is the optimal approach to do this. Here's what I have in mind:
My app uses a KodeinAware application class. The served Kodein instance holds all dependencies required by my app.
In my tests I would like to inject mocked overrides for specific dependencies to test behavior of the app in various situations.
Overrides should be different for each test, and should be injected before/while the test runs.
Is the configurable Kodein extension sensible in this situation, or is there a simpler, better suited approach (and if so, which)?
If your test is given a Kodein instance (meaning that it can use a different Kodein object than the one held by your Application), then the recommended approach is to create a new Kodein object that extends the one of the app and overrides all necessary bindings.
val testKodein = Kodein {
extend(appKodein())
bind<MyManager>(overrides = true) with singleton { mock<MyManager>() }
}
The configurable Kodein option is recommended only if you're using a static "one true Kodein". Using it prevents the possibility to run you're tests in parallel (because they all access the same Kodein instance), and forces you to clear the ConfigurableKodein between each tests and re-declare every time different overrides.
I am now using the ConfigurableKodein inside my custom App class.
class App : Application(), KodeinAware {
override val kodein = ConfigurableKodein()
override fun onCreate() {
super.onCreate()
// A function is used to create a Kodein module with all app deps.
kodein.addImport(appDependencies(this))
}
}
// Helper for accessing the App from any context.
fun Context.asApp() = this.applicationContext as App
Inside my AppTestRunner class, I declare the configuration to be mutable. That way I can reset it's configuration between each and every test.
class AppTestRunner : AndroidJUnitRunner() {
override fun callApplicationOnCreate(app: Application) {
app.asApp().kodein.mutable = true
super.callApplicationOnCreate(app)
}
}
I have created a JUnit rule that reset the dependency graph before every test.
class ResetKodeinRule : ExternalResource() {
override fun before() {
val app = InstrumentationRegistry.getInstrumentation().targetContext.asApp()
app.kodein.clear()
app.kodein.addImport(appDependencies(app))
}
}
In my tests I can now retrieve the App.kodein instance and inject mocks that override dependencies of the original graph. The only thing that needs to be guaranteed is that the tested activity is launched after configuring mocks, or behavior is not predictable.

Android plain Junit with Dagger 2

I used to work in MVP and I usually test my presenters using a plain Junit (Not the Instrumentation !) , since Presenters only have the business logic and no references to Android internals whatsoever.
Now by switching to Dagger 2 , I understood that I have a problem setting up a "TestModule" for my app component.
Creating a component will not work from within a test class (probably because "apt" is not running there)
Didn't find any examples for using Dagger with a standard Junit testing. Every example I have found only relies on Instrumentation testing or Roboelectric (which basically mocks Activities and other Android related stuff) , but this is just a UI testing for me , and I don't need that.
Just to make things clear , I am talking about the tests that are located at app->src->test folder not the app->src->androidTest !
So do I do something wrong ? Or missing something ? Can anyone explain or give examples on how to use Dagger 2 in normal unit tests ?
I'm not sure if my solution will work for you but I see no reason it shouldn't.
First I created testInjectionComponent
#Singleton
#Component(modules = {MockNetworkModule.class})
public interface MockInjectionComponent extends InjectionComponent {
void inject(DaggerUnitTest daggerUnitTest);
}
Then my Unit Tests I add injection in the before method. like so:
#Before
public void setUp() throws Exception {
MockInjectionComponent mockInjectionComponent = DaggerMockInjectionComponent
.builder()
.mockNetworkModule(new MockNetworkModule())
.build();
mockInjectionComponent.inject(this);
}
Then I just Annotate my Injected Object.
EDIT :
Do not forget to add testApt "com.google.dagger:dagger-compiler:$daggerVersion" at your app.gradle file .
As mentioned by the accepted answer. Do not forget to add :
For Java
Android Test
androidTestAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'
JUnit test
testAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'
For Kotlin
Android Test
kaptAndroidTest 'com.google.dagger:dagger-compiler:$dagger_version'
JUnit test
kaptTest 'com.google.dagger:dagger-compiler:$dagger_version'
You don't need any dagger to test your presenter. Dagger's job is it to fullfill the dependencies of your classes (dependency injection).
For example you have this Presenter:
public class MyPresenter {
Database database;
ApiService apiService;
#Inject
public MyPresenter(final Database database, final ApiService apiService) {
this.database = database;
this.apiService = apiService;
}
}
Dagger will provide your Presenter with the database and apiService objects for your presenter to use them. When running the actual app (not a test) these will be real objects with real functionality.
When testing the presenter, you want to test only the presenter, everything else should be mocked.
So when you create the presenter in your PresenterTest, you create it with mocked versions of database and apiService.
You can then test how your presenter interacts with these object by
a. mocking the objects behaviour like
when(database.getSomething()).thenReturn(something)
b. verify your presenter does what you want it to do with these objects like
verify(database).saveSomething()
(pseudo code)
Standard way to mock would be Mockito.
You can swap out real modules with fake modules in two ways: do it at compile time using flavors as recommended by google architecture samples or at runtime by creating an abstract method which injects the real thing in production code and fake dependencies in test code. In the second case your test has to subclass the class that you want to mock and build the component from scrach

Categories

Resources