Search code examples
androidmockitointegration-testingdagger-2dagger

Android Integration test: Mocking the logic/ method in within an activity using Dagger and Mockito and get the returned value back within the test


I am developing an Android application using Kotlin. I am also writing integrated tests for my application using Dagger and Expresso. I am now having a problem with mocking a logic. First, let me show you how I mock logic in the code using Dagger for the integrated tests.

I have the Dagger app component class for the test with the following definition

@Singleton
@Component(modules = [ TestAppModule::class ])
interface TestAppComponent : AppComponent //App component is the actual class used for the application
{
    fun inject(app: MainActivity)
}

This is the definition of the TestAppModule class

@Module
open class TestAppModule (private val app: Application) {
    @Singleton
    @Provides
    open fun fileIOService(): IFileIOService {
        return FakeFileIOService()
    }
}

I also have a class called AppModule for the actual Application code with the following definition.

@Module
open class TestAppModule (private val app: Application) {
    @Singleton
    @Provides
    open fun fileIOService(): IFileIOService {
        return FakeFileIOService()
    }
}

The idea is that the integration tests will use the TestAppModule and so the FakeFileIOService class will be injected. For the actual application, it will use the AppModule and so the FileIOService class will be injected instead. Both the FakeFileIOService class and the FileIOService class will inherit from the IFileIOService interface. The IFileIOService interface will have the following definition.

interface IFileIOService
{
    fun saveFile(data: String)
}

The FileIOService class and the FakeIOService class will have their own version of the implementation for the saveFile method. The FakeIOService will have the mock version.

I will have a property for IFileIOService in the MainActivity class for dependency injection.

class MainActivity: AppCompatActivity()
{
    @Inject
    lateinit var fileIOService: IFileIOService
}

As I mentioned, the FakeFileIOService class will be injected for the integration tests. The code works fine. It uses the fake version for the integration tests and the concrete for the actual application.

But now, I am having a problem where I need to mock the logic of the method using Mockito and get the returned value within the tests.

But, now I am having an issue with mocking the logic using Mokito to test File and getting the mocked value back within the tests.

Following is the implementation of my

fun saveFile(data: String) {
    FileOutputStream mockFos = Mockito.mock(FileOutputStream.class);
    mockFos.write(data.getBytes());
    mockFos.close();
}

Literally, I am trying to mock the file logic using Mockito. Then I am trying to write an integration test something like this.

@get:Rule
var mainActivityRule: ActivityTestRule<MainActivity> = ActivityTestRule<MainActivity>(CameraActivity::class.java, true, false)

@Test
fun fileIsSaveInWhenButtonIsClicked() {
    Intents.init()
    var targetContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
    var intent: Intent = Intent(targetContext, MainActivity::class.java)
    this.mainActivityRule.launchActivity(null) 
    onView(withId(R.id.button_save_file)).perform(click())

    //here I want to assert if the file is saved using Mockito something like this
    //verify(mockFos).write(data.getBytes()); the issue is that I cannot get the mockFos instance creatd in the saveFile method of the FakeFileIOService class
    Intents.release()
}

See the comment in the test. The issue is that I want to verify if the file is saved using Mockito. But the issue is that I cannot get the mockFos instance created in the saveFile of the FakeIOService class. How can I test it? What would be the best way to test this scenario?


Solution

  • You are not supposed to mock system logic. Writing to a file and closing the file is system logic. You are only supposed to mock your own logic.

    And what you should be verifying is that the saveFile method was called. Not that the content of saveMyFile was invoked.

    val mock = mock(IFileIOService::class.java)
    mainActivity.fileIOService = mock
    
    //do something that in return does fileIOService.saveFile
    
    verify(mock).saveFile(any())
    

    Another option is to spy:

     val spy = spy(mainActivity.fileIOService)
     //do something that in return does fileIOService.saveFile
    
     verify(spy).saveFile(any())