【发布时间】:2018-09-13 20:20:13
【问题描述】:
我是 Android 单元测试的新手,并且已经阅读了几个教程来熟悉 mockito 和 robolectric。
我的应用程序正在使用 Dagger 2 将我的 EventService 注入我的 MainActivity。对于我的MainActivityUnitTest,我设置了一个TestServicesModule 来提供EventService 的模拟版本,以便我可以使用Robolectric 对我的MainActivity 运行单元测试
在单元测试中执行我的EventService.getAllEvents(callback: ServiceCallback) 上的ServiceCallback 时遇到问题。我已经在 MainActivityUnitTest 类的 @Setup 中验证了 EventService 正在作为模拟对象注入。我已经阅读了几篇教程和博客文章,据我所知,我做的一切都是正确的。 MainActivity 中的refreshData() 函数被成功调用,我可以看到正在执行对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)
}
}
编辑:添加 App 和 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()
}
}
【问题讨论】:
标签: android kotlin mockito dagger-2 robolectric