Android Kotlin: интеграционные тесты с использованием Dagger 2 и Mockito выдают ошибку, «ноль взаимодействий с этим макетом» - PullRequest
0 голосов
/ 16 февраля 2020

Я разрабатываю приложение Android с использованием Kotlin. Я пишу интеграционные тесты для моего приложения. Теперь у меня возникла проблема с имитацией внедренного класса зависимостей, который вводится с помощью теста Dagger 2, если вызывается метод смоделированного объекта. Ниже приведен мой код.

У меня есть класс TestAppComponent со следующим кодом

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

   fun inject(app: MockApplicationController)
}

Как вы можете видеть в классе, у меня есть два метода ввода. Один для ApplicationController, который в основном является классом приложения, используемым для реального приложения. Еще один контроллер MockAppllication, который также является классом приложения, но используется для тестов.

Ниже приведена реализация класса ApplicationController

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)
    }
}

Как видно из кода, Я инициализирую класс Dagger AppComponent в методе onCreate класса приложения. Затем введите класс зависимости.

Это реализация моего класса MockApplicationController, который используется для тестов.

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)
    }
}

Это мой класс активности.

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)
        }
     }
}

Как вы можете видеть, в основном то, что я делаю в классе активности, это то, что я просто вызываю saveFile интерфейса IFileService.

Ниже приведено определение интерфейса IFileService

interface IFileService
{
    fun saveFile(path: String)
}

Есть два класса, которые реализуют IFileService. Одним из них является класс FakeFileService, который будет использоваться для тестов, а другим - класс FileService, который будет использоваться для реального приложения.

Ниже приведена реализация класса FakeFileService

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

I также создал два класса для модулей приложения Dagger. Один для тестов, а другой для реального приложения.

Ниже приведена реализация класса TestAppModule, который будет использоваться для тестов.

@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()
    }
}

Обратите внимание на метод injectFileService. Я создал этот метод для внедрения в тесты фиктивного объекта / экземпляра с использованием Mockito.

Это мой тестовый класс

@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()
    }
}

Как вы можете видеть, в моем тестовом классе я пытаюсь внедрить проверенную версию объекта IFileService до запуска теста. Затем в моем тесте я утверждаю, что метод выполняется. Когда я запускаю тест, он жалуется, что «ноль взаимодействий с этим макетом». Как я могу это исправить? Что не так с моим кодом и как я могу это исправить?

Это мой класс ApplicationController

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)
    }
}

Когда я помещаю loggin в каждый метод onCreate классов контроллера приложения и метод @Before они вызываются в следующем порядке.

  1. ApplicationController
  2. MockApplicationController
  3. Код для изменения MockApplicationController с помощью метода @Before
...