All Projects → fabioCollini → Daggermock

fabioCollini / Daggermock

Licence: apache-2.0
A JUnit rule to easily override Dagger 2 objects

Programming Languages

java
68154 projects - #9 most used programming language

Projects that are alternatives of or similar to Daggermock

Marvel
Marvel Characters Android Application Assigned by smava GmbH
Stars: ✭ 227 (-80.36%)
Mutual labels:  dagger, espresso, mockito
TeamCityApp
TeamCity in your pocket (Android application)
Stars: ✭ 48 (-95.85%)
Mutual labels:  mockito, espresso
AndroidVIP
Android project to experiment the VIPER approach using mosby, RxJava and dagger2
Stars: ✭ 21 (-98.18%)
Mutual labels:  dagger, mockito
Android tmdb clean architecture
Showcase of clean architecture concepts along with Continuous Integration and Development for modular Android applications. Includes test suits (functional and unit tests) along with code coverage.
Stars: ✭ 63 (-94.55%)
Mutual labels:  espresso, mockito
Star Wars Shop
Simple project with clean architecture and android lifecycle
Stars: ✭ 37 (-96.8%)
Mutual labels:  dagger, mockito
mvp-sample
Demonstrates how to implement MVP (Model View Presenter) pattern using Kotlin, RXJava, Retrofit, Dagger and DataBinding
Stars: ✭ 35 (-96.97%)
Mutual labels:  mockito, espresso
GuildWars2 APIViewer
Guild Wars 2 API Viewer: An Android application used for viewing various Guild Wars 2 API endpoint responses. Developed utilizing MVVM architecture, in conjunction with Databinding, Dagger 2, Retrofit 2, and RxJava 2.
Stars: ✭ 53 (-95.42%)
Mutual labels:  mockito, espresso
TDDWeatherApp
Android App trying to apply TDD and using MVVM, Kotlin Coroutines
Stars: ✭ 38 (-96.71%)
Mutual labels:  mockito, espresso
Avenging
MVP pattern example on Android: no Dagger or RxJava example
Stars: ✭ 279 (-75.87%)
Mutual labels:  espresso, mockito
Android Starter
[Android Architecture] Android starter based on MVP/Dagger2/RxJava2/Robolectric/Espresso/Mockito. It provides a generator to fast create a Android template project.
Stars: ✭ 522 (-54.84%)
Mutual labels:  espresso, mockito
Kotlin Android Starter
[Kotlin Android] Kotlin Android starter based MVP/Dagger2/RxJava2/Robolectric/Espresso/Mockito. It provides a generator to fast create a Kotlin Android project.
Stars: ✭ 589 (-49.05%)
Mutual labels:  espresso, mockito
Mvvm Kotlin Android Architecture
MVVM + Kotlin + Retrofit2 + Hilt + Coroutines + Kotlin Flow + mockK + Espresso + Junit5
Stars: ✭ 1,014 (-12.28%)
Mutual labels:  espresso, mockito
Mvpandroid
Sample app to demonstrate MVP (Model - View - Presenter) architecture in android
Stars: ✭ 91 (-92.13%)
Mutual labels:  espresso, mockito
Starwars-clean
Simple project with clean architecture
Stars: ✭ 34 (-97.06%)
Mutual labels:  dagger, mockito
Newspaper
An aggregated newspaper app containing news from 10+ local news publishers in Hong Kong. Made with ❤
Stars: ✭ 82 (-92.91%)
Mutual labels:  espresso, mockito
AndroidMVPArchitecture
Android MVP architecture sample project with or without RxJava and Dagger2 and Kotlin
Stars: ✭ 78 (-93.25%)
Mutual labels:  dagger, mockito
Katasuperheroesandroid
Super Heroes Kata for Android Developers. The main goal is to practice UI Testing.
Stars: ✭ 680 (-41.18%)
Mutual labels:  dagger, espresso
Githubprojectbrowser
This is a sample Android Project that is based on Clean Architecture
Stars: ✭ 64 (-94.46%)
Mutual labels:  espresso, mockito
Tdcapp
Sample app which access the TDC (The Developer's Conference) REST API.
Stars: ✭ 55 (-95.24%)
Mutual labels:  mockito
Kuiks
Kotlin Multiplatform based testing framework for Android and iOS
Stars: ✭ 38 (-96.71%)
Mutual labels:  espresso

DaggerMock

A JUnit rule to easily override Dagger 2 objects

Build Status codecov Android Arsenal

More info about testing using Dagger 2 and Mockito are available in this Medium post.

Overriding an object managed by Dagger 2 is not easy, you need to define a TestModule and, if you want to inject your test object, a TestComponent.

Using a DaggerMockRule it's possible to override easily (in Java or Kotlin) the objects defined in a Dagger module:

Java
public class MainServiceTest {

    @Rule public DaggerMockRule<MyComponent> rule = new DaggerMockRule<>(MyComponent.class, new MyModule())
            .set(new DaggerMockRule.ComponentSetter<MyComponent>() {
                @Override public void setComponent(MyComponent component) {
                    mainService = component.mainService();
                }
            });

    @Mock RestService restService;

    @Mock MyPrinter myPrinter;

    MainService mainService;

    @Test
    public void testDoSomething() {
        when(restService.getSomething()).thenReturn("abc");

        mainService.doSomething();

        verify(myPrinter).print("ABC");
    }
}
Kotlin
class MainServiceTest {

    @get:Rule val rule = DaggerMock.rule<MyComponent>(MyModule()) {
        set { mainService = it.mainService() }
    }

    val restService: RestService = mock()

    val myPrinter: MyPrinter = mock()

    lateinit var mainService: MainService

    @Test
    fun testDoSomething() {
        whenever(restService.something).thenReturn("abc")

        mainService.doSomething()

        verify(myPrinter).print("ABC")
    }
}

When DaggerMockRule rule is instantiated, it looks for all @Mock annotated fields in your test class and it replaces them with Mockito mocks if there is a provider method in your module for that class. Then it uses all the test fields to override the objects defined in the Dagger configuration.

Note: DaggerMock invokes MockitoAnnotations.initMocks before the test. Hence, just adding the DaggerMockRule is not enough: you additionally need to annotate any field you want to mock with @Mock or @Spy (even if you don't have to define behavior or verify things on the mock).

Note: static and null fields can't be used by DaggerMock.

In this example MyModule contains two methods to provide RestService and MyPrinter objects. Behind the scenes, the DaggerMockRule rule dynamically creates a new module that overrides MyModule, it returns the mocks for restService and myPrinter defined in the test instead of the real objects, like this:

public class TestModule extends MyModule {
    @Override public MyPrinter provideMyPrinter() {
        return Mockito.mock(MyPrinter.class);
    }

    @Override public RestService provideRestService() {
        return Mockito.mock(RestService.class);
    }
}

DaggerMock can't override Dagger objects that are defined using Inject annotation, since version 0.6 you get a runtime error if the test contains a field of a class that is not defined in a module. All the modules containing objects that are going to be replaced must be provided in the DaggerMockRule constructor.

Espresso support

A DaggerMockRule can also be used in an Espresso test:

Java
public class MainActivityTest {

    @Rule public DaggerMockRule<MyComponent> daggerRule = new DaggerMockRule<>(MyComponent.class, new MyModule())
            .set(new DaggerMockRule.ComponentSetter<MyComponent>() {
                @Override public void setComponent(MyComponent component) {
                    App app = (App) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
                    app.setComponent(component);
                }
            });

    @Rule public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class, false, false);

    @Mock RestService restService;

    @Mock MyPrinter myPrinter;

    @Test
    public void testCreateActivity() {
        when(restService.getSomething()).thenReturn("abc");

        activityRule.launchActivity(null);

        verify(myPrinter).print("ABC");
    }
}
Kotlin
class MainActivityTest {

    @get:Rule val daggerRule = DaggerMock.rule<MyComponent>(MyModule()) {
        set {
            val app = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as App
            app.component = it
        }
    }

    @get:Rule var activityRule = ActivityTestRule(MainActivity::class.java, false, false)

    val restService: RestService = mock()

    val myPrinter: MyPrinter = mock()

    @Test fun testCreateActivity() {
        whenever(restService.something).thenReturn("abc")

        activityRule.launchActivity(null)

        verify(myPrinter).print("ABC")
    }
}

In this example the third parameter launchActivity of the ActivityTestRule constructor is set to false to manually launch the Activity. This way it's possible to define behaviour on the mocks before the creation of the Activity, and verify things on your mocks in your tests.

Robolectric support

In a similar way a DaggerMockRule can be used in a Robolectric test:

Java
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {

    @Rule public final DaggerMockRule<MyComponent> rule = new DaggerMockRule<>(MyComponent.class, new MyModule())
            .set(new DaggerMockRule.ComponentSetter<MyComponent>() {
                @Override public void setComponent(MyComponent component) {
                    ((App) RuntimeEnvironment.application).setComponent(component);
                }
            });

    @Mock RestService restService;

    @Mock MyPrinter myPrinter;

    @Test
    public void testCreateActivity() {
        when(restService.getSomething()).thenReturn("abc");

        Robolectric.setupActivity(MainActivity.class);

        verify(myPrinter).print("ABC");
    }
}
Kotlin
@RunWith(RobolectricTestRunner::class)
@Config(constants = BuildConfig::class, sdk = intArrayOf(21))
class MainActivityTest {

    @get:Rule val rule = DaggerMock.rule<MyComponent>(MyModule()) {
        set { (RuntimeEnvironment.application as App).component = it }
    }

    val restService: RestService = mock()

    val myPrinter: MyPrinter = mock()

    @Test fun testCreateActivity() {
        whenever(restService.something).thenReturn("abc")

        Robolectric.setupActivity(MainActivity::class.java)

        verify(myPrinter).print("ABC")
    }
}

Note for Linux and Mac Users: working directory must be manually configured in Android Studio. More info on Robolectric site.

InjectFromComponent annotation

In the first example we have used a ComponentSetter subclass to retrieve an object from the component:

@Rule public DaggerMockRule<MyComponent> rule = new DaggerMockRule<>(MyComponent.class, new MyModule())
        .set(new DaggerMockRule.ComponentSetter<MyComponent>() {
            @Override public void setComponent(MyComponent component) {
                mainService = component.mainService();
            }
        });

MainService mainService;

Since DaggerMock 0.6 this code can be written in an easier way using InjectFromComponent annotation:

Java
public class MainServiceTest {

    @Rule public final DaggerMockRule<MyComponent> rule = new DaggerMockRule<>(MyComponent.class, new MyModule());

    @Mock RestService restService;

    @Mock MyPrinter myPrinter;

    @InjectFromComponent MainService mainService;

    @Test
    public void testDoSomething() {
        when(restService.getSomething()).thenReturn("abc");

        mainService.doSomething();

        verify(myPrinter).print("ABC");
    }
}
Kotlin
class MainServiceTest {

    @get:Rule val rule = DaggerMock.rule<MyComponent>(MyModule())

    val restService: RestService = mock()

    val myPrinter: MyPrinter = mock()

    @InjectFromComponent lateinit var mainService: MainService

    @Test fun testDoSomething() {
        whenever(restService.something).thenReturn("abc")

        mainService.doSomething()

        verify(myPrinter).print("ABC")
    }
}

Many objects managed by Dagger are only injected in other objects and are not exposed in a component. For example if the MainService object is injected in MainActivity we can use the following annotation:

@InjectFromComponent(MainActivity.class) MainService mainService;

A MainActivity object is created using reflection, the inject method is invoked on this object and then the mainService field is extracted and used to populate the test field.

Custom rules

It's easy to create a DaggerMockRule subclass to avoid copy and paste and simplify the test code:

public class MyRule extends DaggerMockRule<MyComponent> {
    public MyRule() {
        super(MyComponent.class, new MyModule());
        set(new DaggerMockRule.ComponentSetter<MyComponent>() {
            @Override public void setComponent(MyComponent component) {
                App app = (App) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
                app.setComponent(component);
            }
        });
    }
}

In Kotlin a method that creates the rule can be easily defined:

fun myRule() =
        DaggerMock.rule<MyComponent>(MyModule()) {
            set {
                val app = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as App
                app.component = it
            }
        }

The final test uses the rule subclass:

public class MainActivityTest {

    @Rule public MyRule daggerRule = new MyRule();

    @Rule public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class, false, false);

    @Mock RestService restService;

    @Mock MyPrinter myPrinter;

    @Test
    public void testCreateActivity() {
        when(restService.getSomething()).thenReturn("abc");

        activityRule.launchActivity(null);

        verify(myPrinter).print("ABC");
    }
}

Supported Dagger features

Subcomponents

Since version 0.6 Dagger subcomponents are supported by DaggerMock with a limitation: subcomponent module must be passed as parameter in subcomponent creation method. For example if the subcomponent is defined as follows:

@Subcomponent(modules = MainActivityModule.class)
public interface MainActivityComponent {
    void inject(MainActivity mainActivity);
}

The method in the main component that creates the subcomponent must be defined using a module parameter:

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    MainActivityComponent activityComponent(MainActivityModule module);
}

Subcomponent support doesn't work on Dagger 2.0, you need to use Dagger version 2.1+. A complete example is available here.

Abstract and static methods in module

DaggerMock doesn't support modules with static or abstract methods (annotated with @Binds). However if a component contains multiple modules (some abstract and other not abstract) DaggerMock can be used to replace objects defined in the not abstract modules.

Dagger Android

Dagger Android is supported with some limitations:

  • JVM tests are not supported, DaggerMock can be used only in Espresso tests
  • objects defined in subcomponent/dependent component cannot be replaced, DaggerMock works only for objects defined in application component
  • application must be set manually using customizeBuilder method:
Java
public class EspressoDaggerMockRule extends DaggerMockRule<AppComponent> {
    public EspressoDaggerMockRule() {
        super(AppComponent.class, new AppModule());
        customizeBuilder(new BuilderCustomizer<AppComponent.Builder>() {
            @Override public AppComponent.Builder customize(AppComponent.Builder builder) {
                return builder.application(getApp());
            }
        });
        set(new DaggerMockRule.ComponentSetter<AppComponent>() {
            @Override public void setComponent(AppComponent component) {
                component.inject(getApp());
            }
        });
    }

    private static App getApp() {
        return (App) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
    }
}
Kotlin
fun espressoDaggerMockRule() = DaggerMock.rule<AppComponent>(AppModule(app)) {
    set { component -> component.inject(app) }
    customizeBuilder<AppComponent.Builder> { it.application(app) }
}

val app: App get() = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as App

A complete example is available here.

Kotlin support

DaggerMock can be used in both Java and Kotlin projects. Kotlin classes are final by default, you need to open them to create mocks using Mockito (and to use DaggerMock). There are three ways to solve this problem:

JitPack configuration

DaggerMock is available on JitPack, add the JitPack repository in your build.gradle (in top level dir):

repositories {
    jcenter()
    maven { url "https://jitpack.io" }
}

and the dependency in the build.gradle of the module:

dependencies {
    testImplementation 'com.github.fabioCollini.daggermock:daggermock:0.8.5'
    //and/or
    androidTestImplementation 'com.github.fabioCollini.daggermock:daggermock:0.8.5'
    
    //kotlin helper methods
    testImplementation 'com.github.fabioCollini.daggermock:daggermock-kotlin:0.8.5'
    //and/or
    androidTestImplementation 'com.github.fabioCollini.daggermock:daggermock-kotlin:0.8.5'
}

License

Copyright 2016-2017 Fabio Collini

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].