androidmockitorobolectricandroid-viewmodelandroid-unit-testing

Android Unit test fails with java.lang.IllegalArgumentException: INTERNET permission is required


I am trying to set up unit tests for my application. But I am facing an issue with Segment

This is my test class:

@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.O_MR1])
class LoginViewModelTest {
    private lateinit var viewModel: LoginViewModel

    @Mock
    private lateinit var authService: AuthService

    @Mock
    private lateinit var sharedPreferences: SharedPreferences

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        val mMyApplication = mock<Application>()
        viewModel = LoginViewModel(mMyApplication, sharedPreferences, authService)
    }

    @Test
    fun test_phone(){
        viewModel.isPhone.observeForTesting {
            val isPhone = LiveDataTestUtil.getValue(viewModel.isPhone) ?: 
                return@observeForTesting
           Assert.assertFalse(isPhone)
        }
    }
}

This is failing with this error:

INTERNET permission is required.
java.lang.IllegalArgumentException: INTERNET permission is required.
    at com.segment.analytics.Analytics$Builder.<init>(Analytics.java:1068)
    at com.app.android.MyApplication.onCreate(MyApplication.kt:42)

MyApplication class is initializing Segment like this:

override fun onCreate() {
val analytics: Analytics = Analytics.Builder(  //This is where its failing
            applicationContext,
            this.getString(R.string.segmentApiKey)
        )
            .trackApplicationLifecycleEvents()
            .build()

        // Set the initialized instance as a globally accessible instance.
        Analytics.setSingletonInstance(analytics)
}

I found this in StackOverflow

where they are suggesting that we have to init application like this:

protected Context instance() {
        ShadowApplication shadowApp = Shadows.shadowOf(this);
        shadowApp.grantPermissions("android.permission.INTERNET");

        return shadowApp.getApplicationContext();
    }

pass instance() to Segment like this:

Analytics.with(instance()).identify("userId", null, null);

I don't think this is the right way because we are altering the code for the Unit test which is not the correct way IMHO.

I tried it anyway, but it's failing because it cannot resolve shadowApp.getApplicationContext()

I am using shadowApplication by adding this

implementation 'org.robolectric:shadow-framework:4.6'

Please help me figure this out. Thanks in advance.

If using ShadowApplication is the right way, please help me understand what is happening too. My understanding for this code snippet is that it is a sample Application class or something that can be used for Testing. Please cmiiw.

Edit: My AndroidManifest.xml already has internet permission <uses-permission android:name="android.permission.INTERNET"


Solution

  • Well apparently there was something wrong with my setup

    I added these

    // Required -- JUnit 4 framework
    testImplementation 'junit:junit:4.13.2'
    // Optional -- Robolectric environment
    testImplementation 'androidx.test:core:1.4.0'
    // Optional -- Mockito framework
    testImplementation 'org.mockito:mockito-core:4.5.0'
    // Optional -- mockito-kotlin
    testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
    // Optional -- Mockk framework
    testImplementation 'io.mockk:mockk:1.12.3'
    testImplementation 'androidx.test.ext:junit-ktx:1.1.3'
    testImplementation 'org.robolectric:robolectric:4.6.1'
    testImplementation 'org.mockito:mockito-inline:2.13.0'
    testImplementation 'android.arch.core:core-testing:1.1.1'
    

    and changed my test class to be like this:

    @RunWith(MockitoJUnitRunner::class)
    class LoginViewModelTest : TestCase() {
        @get:Rule
        var instantExecutorRule = InstantTaskExecutorRule()
        private lateinit var viewModel: LoginViewModel
    
        @Before
        public override fun setUp() {
            super.setUp()
            val mMyApplication = mock(Application::class.java)
            val sharedPreferences = mock(SharedPreferences::class.java)
            val authService = mock(AuthService::class.java)
            val analyticsFacade = mock(AnalyticsFacadeImpl::class.java)
            doNothing().`when`(analyticsFacade).trackEvent(anyString(), any())
            viewModel = LoginViewModel(
            mMyApplication, sharedPreferences, authService,
                 analyticsFacade as AnalyticsFacadeImpl)
        }
    
        @Test
        fun test_phone(){
            viewModel.isPhone.observeForTesting {
                val isPhone = LiveDataTestUtil.getValue(viewModel.isPhone) ?: 
                    return@observeForTesting
               Assert.assertFalse(isPhone)
            }
        }
    }