10

I would like to use my realm manager into unit test module. I did

@Singleton
@Component(modules = {
        TestApplicationModule.class,
        AndroidSupportInjectionModule.class,
        TestStoreDataModule.class,
        TestUtilsModule.class})
public interface AppComponentTest extends AppComponent {

    @Component.Builder
    interface Builder {

        @BindsInstance
        AppComponentTest.Builder application(Application application);

        AppComponentTest build();
    }
}

and then I want to achieve

@RunWith(RobolectricTestRunner.class)
@Config(application = TestVerioriApplication.class, sdk=27)
public class BaseVerificationQuestionnaireFragmentTest {

    @Inject
    RealmManager realmManager;
}

But realmManager is null. How to use dagger 2 to write simple module test ? I used dagger-mock but it does not help. My module contains

@Module(includes = StoreDataModule.class)
public class TestStoreDataModule {

    @Provides
    @Singleton
    public static RealmConfiguration provideRealmConfiguration(RealmConstants realmConstants) {
        return new RealmConfiguration.Builder()
                .name(realmConstants.getName())
                .encryptionKey("Implement this key".getBytes())
                .schemaVersion(realmConstants.getSchemaVersion())
                .build();
    }

    @Provides
    @Singleton
    public static RealmManager provideRealmManager(RealmConfiguration realmConfiguration, SchedulerProvider schedulerProvider) {
        return new RealmManager(realmConfiguration, schedulerProvider);
    }

}

I tried everything from google, but I don't know how to inject object from graph.

Piotr Badura
  • 1,574
  • 1
  • 13
  • 17

2 Answers2

1

Override your Application class, where you will replace dagger component instance by your TestComponent. Then create your own test runner by overriding AndroidJUnitRunner class where you need to add test application:

class TestRunner : AndroidJUnitRunner() {
   @Throws(InstantiationException::class,IllegalAccessException::class,ClassNotFoundException::class)
 override fun newApplication(cl:ClassLoader,className:String, context:Context):Application {
        return super.newApplication(cl, TestApplication::class.java.name, context)
  }
}

Next register your runner in build.gradle file :

testInstrumentationRunner "com.test.YourTestRunner"

Now you can just replace the implementation of a module that you want to change in test in your test component.

Sattar
  • 2,453
  • 2
  • 33
  • 47
Karol Kulbaka
  • 1,136
  • 11
  • 21
  • When I use roboelectric I should use AndroidJUnitRunner ? I just only write simple unit tests using dagger 2. – Piotr Badura Nov 02 '18 at 12:51
  • To be honest I have never used Roboelectic. Try to extend RobolectricTestRunner, maybe it will work too. – Karol Kulbaka Nov 02 '18 at 13:27
  • 1
    You should run Robolectric when you don't want to use a real device for testing and stay in the Unit test world. It basically mocks Android components for you so you don't have to. If you put a Robolectric test runner in build.gradle, most of your "real device" tests will fail. So don't do that unless you're 100% aware of what you're changing :) – milosmns Sep 28 '19 at 19:11
  • Hello @KarolKulbaka did it work for you? I have the same use case. – GuilhE Jan 08 '20 at 16:49
  • @GuilhE As I said I've never try it with RobolectricRunner but I don't see any reason why it should not work. Btw what case you are talking about? I've written dozens of android test and I've never met such usecase. – Karol Kulbaka Jan 08 '20 at 18:03
  • Actually my question was for Piotr Badura, my mistake sorry, just noticed now :/ But since you've experience in testing (mine is really poor, trying to change that) I was wondering if using a TestRunner (even if I don't need "Android things") is the key. Because I want to use my Dagger configurations do @Inject an object fully initialized (and its dependencies and so on), which is something that is not happening (maybe my concepts are mixed up and that instance being null is expected). Another question is, why do we need a TestAplication can't we just use the Application? – GuilhE Jan 09 '20 at 10:09
  • 1
    I'll try to speak as clear as it is possible: TestApplication is used to apply test dependencies with dagger (fakes). It is used by working app in test (on device/emulator) to avoid for example network connection. Robolectric is used when you dont need to launch app (activities), for example some unit test that work directly on android classes. Instead of dagger much easier is use mockito or mockk to provide dependencies in robolectric test. – Karol Kulbaka Jan 09 '20 at 11:28
0

Ok. BaseVerificationQuestionnaireFragmentTest requests a field injection, but it is not injected itself. For this, AppComponentTest interface must have this1 line:

public void inject(BaseVerificationQuestionnaireFragmentTest baseVerificationQuestionnaireFragmentTest);

And then this method must be used in somewhere like @Before:

TestApplication app = (TestApplication) RuntimeEnvironment.systemContext;
app.appComponentTest.inject(this);

That assumes appComponentTest is public in TestApplication.

I successfully injected a ViewModel in its test this way.

Pardon my Java.


1 In my case Dagger stuff was located in sharedTest directory and could not see BaseVerificationQuestionnaireFragmentTest. I created an InjectableTest class that my test inherited from, and requested injection in the init block of the InjectableTest (that was Kotlin code).

pratclot
  • 326
  • 2
  • 8