我是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?>)
}
我激活了。
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()
}
}
我认识你。
@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()
}
}
看起来你正在使用不同的组件来注入你的测试和活动。由于它们是不同的组件,我怀疑您使用的是两个不同的eventsService实例。
您的测试使用本地DaggerTestAppComponent。
@Inject lateinit var eventsService: EventsService
@Before
fun setup() {
val testComponent = DaggerTestAppComponent.builder().build()
testComponent.inject(this)
}
您的Activity使用应用程序中的appComponent。
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)
...
}
要解决此问题,您可以考虑添加应用程序类的测试版本,这将允许您使用TestAppComponent替换应用程序中的AppComponent。 Robolectric应该允许您按如下方式创建测试应用程序:http://robolectric.org/custom-test-runner/