Karate_Dog
Karate_Dog

Reputation: 1277

Android mocking applicationcontext inside application

I am having trouble creating a test class. Basically I want to test my view model class which performs network call. The class network component is injected by using dagger needs the context parameter to check connection, this is my view model:

class MyViewModel(application: Application): AndroidViewModel(application) {
   @Inject lateinit var network: NetworkService
    init {
        DaggerNetworkComponent.builder().networkModule(NetworkModule(application.applicationContext))
        .build().inject(this)
        network.callNetwork()
    } 
}

And the test class is something like

lateinit var myViewModel: MyViewModel
@Test
fun testMyNetwork() { 
   application =  Mockito.mock(Application::class.java)
   myViewModel = MyViewModel(application)
}

The application.applicationContext always returns null which then returns IllegalStateException

Is there any solution for this?

Upvotes: 5

Views: 7803

Answers (2)

raditya gumay
raditya gumay

Reputation: 3011

This can be achieved by using a Fake. supposed you want to mock the interface that being implemented by the Application class.

// main
interface AppInitializer {
   val isUserLoggedIn: Boolean
}

class MyApplication : AppInitializer {
    override val isUserLoggedIn: Boolean
        get() = // ...
}

// test
class FakeMyApplication : AppInitializer {
    override val isUserLoggedIn: Boolean
        get() = true

   // you need to override this
   override fun getApplicationContext(): Context {
        return this
    }
}

Upvotes: 0

ror
ror

Reputation: 3500

I think there are two possibilities here. I used 2nd one and know it works, 1st one is my guess.

(1)

You are mocking Application. Then you are trying to use its applicationContext however you did not mock it. You need to mock it as well:

val context  =  Mockito.mock(Context::class.java)
Mockito.`when`(application.applicationContext).thenReturn(context)

(2)

Assume 1) your test is instrumentation test 2) you are using subclass of Application - say, MyApplication.

You will then have to create its subclass - say, TestMyApplication. Next step would be to create subclass of AndroidJUnitRunner, may be like this:

public class MyInstrumentationTestRunner extends AndroidJUnitRunner {
    @Override
    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return Instrumentation.newApplication(TestMyApplication.class, context);
    }
}

Finally, you need to tell android to use this runner:

allprojects {
    ext {
        ....
        testInstrumentationRunner = "com...MyInstrumentationTestRunner"
    }
}

As a result of the above, application context will be instance of TestMyApplication across your instrumentation tests.

Upvotes: 3

Related Questions