Android Kotlin: integration tests using Dagger 2 and Mockito getting error, “zero interactions with this mock”

試著忘記壹切 提交于 2020-03-04 19:36:08

问题


I am developing an Android application using Kotlin. I am writing integration tests for my application. Now I am having a problem mocking an injected dependency class that is injected using the Dagger 2 test if a method of the mocked object is called. Following is my code.

I have the TestAppComponent class with the following code

@Singleton
@Component(modules = [ TestAppModule::class ])
interface TestAppComponent
{
   fun inject(app: ApplicationController)

   fun inject(app: MockApplicationController)
}

As you can see in the class, I have two inject methods. One for the ApplicationController which basically is the application class used for the actual application. Another one for MockAppllication controller which is also the application class but it is used for the tests.

Following is the implementation of the ApplicationController class

open class ApplicationController: Application()
{
    lateinit var appComponent: AppComponent
    private fun initDagger(app: ApplicationController): AppComponent = DaggerAppComponent
        .builder()
        .appModule(AppModule(app)).build()
    @Inject
    lateinit var fileService: IFileService

    override fun onCreate() {
        super.onCreate()
        appComponent = initDagger(this)
        instance = this
        this.appComponent.inject(this)
    }
}

As you can see in the code, I am initializing the Dagger AppComponent class in the onCreate method of the application class. Then inject the dependency class.

This is the implementation of my MockApplicationController class which is used for the tests.

class MockApplicationController: ApplicationController()
{
    private fun initDagger(app: MockApplicationController): AppComponent = DaggerTestAppComponent
        .builder()
        .testAppModule(TestAppModule(app))
        .build()

    override fun onCreate() {
        super.onCreate()
        //override the dagger app component appComponent
        this.appComponent = initDagger(this)
        instance = this
        this.appComponent.inject(this)
    }
}

This is my activity class.

class MainActivity: AppCompatActivity()
{
     override fun onCreate(savedInstanceState: Bundle?) {
        //rest of the code
        button_save_file.setOnClickListener {
            //rest of the code
            ApplicationController.instance.fileService.saveFile(filePath)
        }
     }
}

As you can see, basically, what I am doing within the activity class is that I am just calling the saveFile of the IFileService interface.

Following is the definition of the IFileService interface

interface IFileService
{
    fun saveFile(path: String)
}

There are two classes that are implementing the IFileService. One is FakeFileService class which will be used for the tests and the other one is FileService class which will be used for the actual application.

Following is the implementation of FakeFileService class

class FakeFileService: IFileService
{
   fun saveFile(path: String)
   {
      //literally, this is doing nothing since we will only test that if the method is called
   }
}

I also created two classes for the Dagger app modules. One for the tests and the other one for the actual application.

Following is the implementation of the TestAppModule class which will be used for tests.

@Module
open class TestAppModule (private val app: Application) {

    private var fileService: IFileService? = null

    @Singleton
    @Provides
    fun provideContext(): Context = app

    //to override injecting the Mockito mock instance
    fun injectFileService(fileService: IFileService) {
        this.fileService = fileService
    }


    @Singleton
    @Provides
    open fun fileService(): IFileService {
        if (this.fileService != null) {
            return this.fileService as IFileService
        }
        return FakeFileService()
    }
}

Please, pay attention to the injectFileService method. I created that method to inject the mock object/ instance mocked using Mockito within the tests.

This is my test class

@RunWith(AndroidJUnit4::class)
@LargeTest
class CameraTest: TestBuilder()
{
    @get:Rule
    var mainActivityRule: ActivityTestRule<MainActivity> = ActivityTestRule<MainActivity>(CameraActivity::class.java, true, false)

    private lateinit var fileServiceMock: IFileIOService

    @Before
    fun setUp() {
        this.fileServiceMock = mock(IFileService::class.java)

        MockitoAnnotations.initMocks(this)
        val instrumentation = InstrumentationRegistry.getInstrumentation()
        val app = instrumentation.targetContext.applicationContext as MockApplicationController
        var testModule = TestAppModule(app)
        testModule.injectFileService(this.fileServiceMock)
        app.appComponent = DaggerTestAppComponent
            .builder()
            .testAppModule(testModule)
            .build()
        app.appComponent.inject(app)
    }

    @Test
    fun itSavesFileWhenButtonIsClicked() {
        Intents.init()
        var targetContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
        var intent: Intent = Intent(targetContext, MainActivity::class.java)
        this.mainActivityRule.launchActivity(intent)        

        onView(withId(R.id.button_save_file)).perform(click())
        verify(this.fileServiceMock).saveFile(any())
        Intents.release()
    }
}

As you can see, in my test class, I try to inject the mocked version of IFileService object before the test is run. Then in my test, I am asserting that the method is executed. When I run the test it is complaining that "zero interactions with this mock". How can I fix it? What is wrong with my code and how can I fix it?

This is my ApplicationController class

open class ApplicationController: Application()
{
    lateinit var appComponent: AppComponent
    private fun initDagger(app: ApplicationController): AppComponent = DaggerAppComponent
        .builder()
        .appModule(AppModule(app)).build()
    @Inject
    lateinit var fileService: IFileService

    override fun onCreate() {
        super.onCreate()
        appComponent = initDagger(this)
        instance = this
        this.appComponent.inject(this)
    }
}

When I put the loggin in each onCreate method of application controller classes and @Before method, they are called in the following order.

  1. ApplicationController
  2. MockApplicationController
  3. The code to modify the MockApplicationController with the @Before method

来源:https://stackoverflow.com/questions/60251133/android-kotlin-integration-tests-using-dagger-2-and-mockito-getting-error-zer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!