4

I am using MVP pattern with a Fragment(GalleryFragment), where Application class(MainApplication) sources MainActivityRepository and GalleryFragmentPresenter(grouped as DIModules) which are provided to Fragment through field injection.

To test GalleryFragment in isolation, my idea was to use Robolectric configuration(@Config) to replace MainApplication entirely with a custom TestApplication sourcing mockDIModules.

GalleryFragmentTest runs until startFragment(galleryFragment) but I get a NullPointerException at MainApplication.getComponent().inject(this); inside GalleryFragment.

I suspect this is because this line specifically uses MainApplication while everything else is dealt with TestApplication set by Robolectric @Config, but I'm not sure and I am looking for advice on how to successfully run tests using this custom TestApplication.

While searching for possible solutions, I found out about using AndroidInjector from Dagger support library, which will get rid of MainApplication.getComponent().inject(this); entirely but would this work? https://android.jlelse.eu/android-and-dagger-2-10-androidinjector-5e9c523679a3

GalleryFragment.java

public class GalleryFragment extends Fragment {
    @Inject
    public MainActivityRepository mRepository;
    @Inject
    public GalleryFragmentPresenter mGalleryFragmentPresenter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MainApplication.getComponent().inject(this);   //NullPointerException here
        mGalleryFragmentPresenter.initialize(mRepository, value);
    }
}

DIModules.java

@Module
public class DIModules {
    @Provides
    public GalleryFragmentPresenter provideGalleryFragmentPresenter(){
        return new GalleryFragmentPresenter();
    }
    @Provides
    @Singleton
    public MainActivityRepository provideMainActivityRepository(){
        return new MainActivityRepository();
    }
}

AppComponent.java

@Singleton
@Component(modules = DIModules.class)
public interface AppComponent {
        void inject(GalleryFragment galleryFragment);
}

MainApplication.java

public class MainApplication extends Application {
    public static AppComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        Realm.init(this);
        component = buildComponent();
    }
    public static AppComponent getComponent() {
        return component;
    }
    protected AppComponent buildComponent(){
        return DaggerAppComponent
                .builder()
                .dIModules(new DIModules())
                .build();
    }
}

TestApplication.java

public class TestApplication extends Application {
    public static AppComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        component = buildComponent();
    }
    public static AppComponent getComponent() {
        return component;
    }
    protected AppComponent buildComponent(){
        return DaggerAppComponent.builder()
                .dIModules(new mockDIModules())
                .build();
    }
}

GalleryFragmentTest.java

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class,
        application = TestApplication.class)
public class GalleryFragmentTest {
    @Test
    public void allItemTabTest() throws Exception {
        GalleryFragment galleryFragment = GalleryFragment.newInstance(value);
        startFragment(galleryFragment);
        assertNotNull(galleryFragment);
    }
}

I am using dagger, dagger-android-support, dagger-compiler version 2.14.1 and robolectric:3.6.1

Kei
  • 43
  • 5

1 Answers1

1

Of course, it is null. Your fragment still tries to work with production application while you're doing things in the test application.

Change your injection code to next:

((MainApplication) getContext().getApplicationContext()).getComponent().inject(this);

And also make the method in your Application getComponent() as not static, so test app overrides it.

Another option is to change your TestApplication to next:

public class TestApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        buildComponent();
    }

    private void buildComponent(){
        Application.component = DaggerAppComponent.builder()
                .dIModules(new mockDIModules())
                .build();
    }
}
Roger Alien
  • 3,040
  • 1
  • 36
  • 46
Eugen Martynov
  • 19,888
  • 10
  • 61
  • 114
  • I got ClassCastException after making the changes but extending `MainApplication` fixed it. Thanks for the help :) – Kei Jan 30 '18 at 07:44