在注入了模拟对象的Android单元测试中未执行回调

时间:2018-09-13 20:20:13

标签: android kotlin mockito dagger-2 robolectric

我是Android单元测试的新手,并通过了一些教程来熟悉mockito和robolectric。

我的应用正在使用Dagger 2将我的EventService注入我的MainActivity中。对于我的MainActivityUnitTest,我设置了一个TestServicesModule以提供EventService的模拟版本,以便可以使用Robolectric对我的MainActivity进行单元测试。

我在让ServiceCallback上的EventService.getAllEvents(callback: ServiceCallback)在单元测试中执行时遇到问题。我在@Setup类的MainActivityUnitTest中验证了EventService被作为模拟对象注入。我看了几本教程和博客文章,据我所知,我做的一切正确。 refreshData()中的MainActivity函数已成功调用,我可以看到对eventsService.getAllEvents(callback)的调用正在执行。但是doAnswer {} lambda函数永远不会执行。

这是我的相关代码:

AppComponent.kt

@Singleton
@Component(modules = [
    AppModule::class,
    ServicesModule::class,
    FirebaseModule::class
])
interface AppComponent {
    fun inject(target: MainActivity)
}

ServicesModule.kt

@Module
open class ServicesModule {
    @Provides
    @Singleton
    open fun provideEventService(db: FirebaseFirestore): EventsService {
        return EventsServiceImpl(db)
    }
}

EventsService.kt

interface EventsService {
    fun getAllEvents(callback: ServiceCallback<List<Event>>)
    fun getEvent(id: String, callback: ServiceCallback<Event?>)
}

MainActivity.kt

class MainActivity : AppCompatActivity() {
    @Inject lateinit var eventsService: EventsService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        (application as App).appComponent.inject(this)
        ...
    }

    override fun onStart() {
        super.onStart()
        refreshData()
    }

    eventsService.getAllEvents(object: ServiceCallback<List<Event>> {
        override fun onCompletion(result: List<Event>) {
            viewModel.allEvents.value = result
            loading_progress.hide()
        }
    })
}

现在我们开始测试:

TestAppComponent.kt

@Singleton
@Component(modules = [
    TestServicesModule::class
])
interface TestAppComponent : AppComponent {
    fun inject(target: MainActivityUnitTest)
}

TestServicesModule.kt

@Module
class TestServicesModule {

    @Provides
    @Singleton
    fun provideEventsService(): EventsService {
        return mock()
    }
}

MainActivityUnitTest.kt

@RunWith(RobolectricTestRunner::class)
@Config(application = TestApp::class)
class MainActivityUnitTest {

    @Inject lateinit var eventsService: EventsService

    @Before
    fun setup() {
        val testComponent = DaggerTestAppComponent.builder().build()
        testComponent.inject(this)
    }

    @Test
    fun givenActivityStarted_whenLoadFailed_shouldDisplayNoEventsMessage() {
        val events = ArrayList<Event>()

        doAnswer {
            //this block is never hit during debug
            val callback: ServiceCallback<List<Event>> = it.getArgument(0)
            callback.onCompletion(events)
        }.whenever(eventsService).getAllEvents(any())

        val activity = Robolectric.buildActivity(MainActivity::class.java).create().start().visible().get()
        val noEventsView = activity.findViewById(R.id.no_events) as View

        //this always evaluates to null because the callback is never set from the doAnswer lambda
        assertThat(callback).isNotNull()
        verify(callback)!!.onCompletion(events)
        assertThat(noEventsView.visibility).isEqualTo(View.VISIBLE)
    }
}

编辑:添加应用程序和TestApp

open class App : Application() {
    private val TAG = this::class.qualifiedName
    lateinit var appComponent: AppComponent

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

    open fun initDagger(app: App): AppComponent {
        return DaggerAppComponent.builder().appModule(AppModule(app)).build()
    }
}

class TestApp : App() {
    override fun initDagger(app: App): AppComponent {
        return DaggerTestAppComponent.builder().build()
    }
}

0 个答案:

没有答案