Android Testing with Robolectric and Dagger

前端 未结 3 1040
一生所求
一生所求 2021-01-24 23:33

I am trying to write an Android application using Dagger. Trying to follow the TDD approach, I started writing a test for my First activity. For writing tests I am using Robole

3条回答
  •  长发绾君心
    2021-01-25 00:04

    Ok, first off, user2511882 I have tried your solution before posting the question but the thing is, if you look at the structure of TestMyApplication, where I inject the test module, you would see that your suggestion and my previous tries could not work.

    After rethinking the whole problem I have found a solution along the lines of my initial tries and also a more useful solution (as far as I can see it). First off, I do not rely on the TestMyApplication class anymore. Furthermore I had to do some changes to MyApplication class to make it more "test friendly" (without changing its functionality). So MyApplication class looks like this:

    public class MyApplication extends DaggerApplication
    {
       private List modules;
       public MyApplication() {
           modules = new ArrayList();
           modules.add(new AppModule(this));
       }
    
    @Override
    protected List getAppModules() {
        return modules;
    }
    }
    
    
    

    Now I can create the two test modules, one in which I set the behavior to return true when asking for an internet connection and one which will return false for the same query.

    Now, in my test class I would have the following:

    @RunWith(RobolectricTestRunner.class)
    public class SplashScreenActivityTest
    {
        SplashScreenActivity activity;
    
        public void setUpNoInternet()
        {
    // Now I can add the new test module to the application modules to override the real one in the application onCreate() method
            ((MyApplication)Robolectric.application).getAppModules().add(new GeneralUtilsModuleNoInternetConnection());
            activity = Robolectric.buildActivity(SplashScreenActivity.class).create().get();
        }
        public void setUpWithInternet()
        {
            ((MyApplication)Robolectric.application).getAppModules().add(new GeneralUtilsModuleWithInternetConnection());
            activity = Robolectric.buildActivity(SplashScreenActivity.class).create().get();
        }
    
    
        @Test
        public void testOnCreate_whenNoInternetConnection()
        {
            setUpNoInternet();
           
        }
        @Test
        public void testOnCreate_whenThereIsInternetConnection()
        {
            setUpWithInternet();
           
        }
    
    }
    

    This works fine and is along the lines of my initial plan of testing. But I think there is a more elegant solution instead of creating a new test module for each situation. The modified test module looks like this:

    @Module(
        includes = AppModule.class,
        injects = {SplashScreenActivityTest.class,
                SplashScreenActivity.class},
        overrides = true
    )
    public class GeneralUtilsModuleTest
    {
        private  GeneralUtils mockGeneralUtils;
    
        public GeneralUtilsModuleTest() {
            mockGeneralUtils = Mockito.mock(GeneralUtils.class);
        }
    
        @Provides
        @Singleton
        GeneralUtils provideGeneralUtils() {
    
            return mockGeneralUtils;
        }
    
        public GeneralUtils getGeneralUtils()
        {
            return mockGeneralUtils;
        }
    
        public void setGeneralUtils(final GeneralUtils generalUtils)
        {
            this.mockGeneralUtils = generalUtils;
        }
    }
    

    Using this, the Test class looks like this:

        @RunWith(RobolectricTestRunner.class)
    public class SplashScreenActivityTest
    {
        SplashScreenActivity activity;
    
        private GeneralUtilsModuleTest testModule;
        private GeneralUtils generalUtils;
    
        @Before
        public void setUp()
        {
            testModule = new GeneralUtilsModuleTest();
            generalUtils = Mockito.mock(GeneralUtils.class);
        }
    
        public void setUpNoInternet()
        {
            when(generalUtils.isInternetConnection()).thenReturn(false);
            testModule.setGeneralUtils(generalUtils);
            ((MyApplication)Robolectric.application).getAppModules().add(testModule);
            activity = Robolectric.buildActivity(SplashScreenActivity.class).create().get();
        }
        public void setUpWithInternet()
        {
            when(generalUtils.isInternetConnection()).thenReturn(true);
            testModule.setGeneralUtils(generalUtils);
            (MyApplication)Robolectric.application).getAppModules().add(testModule);
            activity = Robolectric.buildActivity(SplashScreenActivity.class).create().get();
        }
        .....(Tests)....
    }
    

    Thank you all for your help and I really hope that this solution will help others achieve better testing on Android.

    提交回复
    热议问题