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

问题描述 投票:0回答:1

我是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()
    }
}
android kotlin mockito dagger-2 robolectric
1个回答
1
投票

看起来你正在使用不同的组件来注入你的测试和活动。由于它们是不同的组件,我怀疑您使用的是两个不同的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/

© www.soinside.com 2019 - 2024. All rights reserved.