Reputation: 258
Okay, so I decided to take a closer look on implementing junit tests in my project. Then I stumbled on an article that writes about how important it is to use dependency injection frameworks such as Dagger2 and Koin to ease testing.
I tried to read - What exactly is DI. I understand it as a way of resolving Class A dependant objects (B class and C class, for instance).
What I do right now usually is this:
In Activity I create ViewModel
. ViewModel
needs to access data, so for data I have SomeRepository
class. Then I usually pass SomeRepository
through ViewModel
constructor or using property injection. As I understand, that is also some type of dependency injection if I'm not mistaken (correct me if I'm wrong).
So, what benefits I'd gain If I'd start using Dagger 2 now? Maybe simple code comparison would make it clearer? Thanks in advance.
Activity:
val someRepository = SomeRepository()
viewModel.init(someRepository)
In ViewModel:
class SomeViewModel : ViewModel {
private lateinit var repository: SomeRepository
fun init(val someRepo: SomeRepository) {
this.repository = someRepo
}
}
Upvotes: 4
Views: 234
Reputation: 297
Pro1: In layman's terms, DI is important to write decoupled code that makes it easy to write unit tests.
Pro2: It also abstracts dependencies of the object from the parent class. For eg, in your use case, Activity
is not directly using SomeRepository
but still needs to know about it. But instead, if you used constructor injection like so:
class SomeViewModel constructor (@Inject someRepository: SomeRepository) : ViewModel {
}
your activity class just needs to deal with the SomeViewModel
object and any dependencies used by SomeViewModel
are none of its concern.
Pro3: DI makes it very easy to create and access singleton objects.
Just define a class like below and you can get a single instance of the MySingleton
class anywhere in your app.
@Singleton
class MySingleton @Inject constructor() {}
OR
@Provides @Singleton mySingleton() {}
Upvotes: 1
Reputation: 11873
Dagger promotes decoupling of dependencies with it's own dependency graph. We need to tell dagger how the classes can be generated e.g. it takes a constructor parameter. Once defined, it travels through all the dependencies and starts creating them from the low level. If we can't inject any dependency in the constructor e.g. Retrofit we can manually tell dagger how to create an instance of if with @Provides
annotation.
Let's see an example:
Dependency #1 - Invididual moduels
@Module
class NetworkModule {
@Provides
fun provideRetrofit() : Retrofit
}
Dependency #2 - Depends on Dependency #1
class ApiService constructor (@Inject retrofit: Retrofit){
}
Dependency #3 - Depends on Dependency #2
class SomeRepository constructor (@Inject apiService: ApiService){
}
Dependency #4 - Depends on Dependency #3
class SomeViewModel constructor (@Inject someRepository: SomeRepository) : ViewModel {
}
Dagger will generate all dependencies in the runtime. When you will run the app, each dependency is ready and can be injected directly in constructor. You are also allowed to Inject any of those dependencies with field injection
class MainAcitivty : AppCompatActivity(){
@Inject someRepository: SomeRepository
}
Cons with your current approact:
val someRepository = SomeRepository()
viewModel.init(someRepository)
SomeRepository()
over and over again in different acitiviesSomeRepository()
has any dependency those are also going to created several timesSomeRepository()
without its dependencies. In unit test, we are interested in testing the unit not it's dependencies. Upvotes: 5