我使用协程和改造创建了一个应用程序,并且运行良好。当我尝试为Presenter创建UT时出现问题。这是我成为主持人的方式:
class MainPresenter : ViewModel() {
private val compositeDisposable = CompositeDisposable()
private val heroesRepository: HeroesRepository = heroesRepositoryModel.instance()
private lateinit var listener: ActivityStatesListener
fun setActivityListener(listener: ActivityStatesListener) {
this.listener = listener
}
fun getHeroesFromRepository(page: Int) {
GlobalScope.launch(Dispatchers.Main) {
try {
val response = heroesRepository.getHeroes(page)
listener.onHeroesReady(response.data.results)
} catch (e: HttpException) {
listener.onError(e.message())
} catch (e: Throwable) {
listener.onError(e.message)
}
}
}
override fun onCleared() {
super.onCleared()
compositeDisposable.dispose()
}
}
我开始为此制作UT,并做了一个小测试,但出现以下错误:java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize
class HeroesDataSourceTest {
val heroesRepository: HeroesRepository = mock(HeroesRepository::class.java)
@Mock
lateinit var activityListener: ActivityStatesListener
val hero = Heroes.Hero(1, "superman", "holasuperman", 1, null, null)
val results = Arrays.asList(hero)
val data = Heroes.Data(results)
val dataResult = Heroes.DataResult(data)
private val mainPresenter = MainPresenter()
@Before
fun initTest() {
MockitoAnnotations.initMocks(this)
}
@Test
fun testLoadInitialSuccess() = runBlocking(Dispatchers.Main) {
`when`(heroesRepository.getHeroes(0)).thenReturn(dataResult)
mainPresenter.getHeroesFromRepository(0)
verify(activityListener).onHeroesReady(dataResult.data.results)
}
}
很明显Dispatcher.Main出现了问题,但我不知道如何解决。
编辑
使用的存储库如下:
class HeroesRepository {
val privateKey = "5009bb73066f50f127907511e70f691cd3f2bb2c"
val publicKey = "51ef4d355f513641b490a80d32503852"
val apiDataSource = DataModule.create()
val pageSize = 20
suspend fun getHeroes(page: Int): Heroes.DataResult {
val now = Date().time.toString()
val hash = generateHash(now + privateKey + publicKey)
val offset: Int = page * pageSize
return apiDataSource.getHeroes(now, publicKey, hash, offset, pageSize).await()
}
fun generateHash(variable: String): String {
val md = MessageDigest.getInstance("MD5")
val digested = md.digest(variable.toByteArray())
return digested.joinToString("") {
String.format("%02x", it)
}
}
}
答案 0 :(得分:0)
我假设heroesRepository.getHeroes(page)
被标记为暂停,因此它将暂停协程并且不会阻塞主线程。
尝试遵循下一种方法:
// add `CoroutineContext` to the constructor to be replaceable from the tests
class MainPresenter(private val uiContext: CoroutineContext = Dispatchers.Main)
: ViewModel(), CoroutineScope {
private var job: Job = Job()
override val coroutineContext: CoroutineContext
get() = uiContext + job
fun getHeroesFromRepository(page: Int) {
// use local scope to launch a coroutine
launch {
try {
val response = heroesRepository.getHeroes(page)
listener.onHeroesReady(response.data.results)
} catch (e: HttpException) {
listener.onError(e.message())
} catch (e: Throwable) {
listener.onError(e.message)
}
}
}
override fun onCleared() {
super.onCleared()
job.cancel()
}
// ...
}
在测试类中,将uiContext
替换为另一个CoroutineContext
:
class HeroesDataSourceTest {
// ... initializations
@Test
fun testLoadInitialSuccess() = runBlocking {
`when`(heroesRepository.getHeroes(0)).thenReturn(dataResult)
mainPresenter = MainPresenter(Dispatchers.Unconfined).apply {
getHeroesFromRepository(0)
}
// ... your tests here
}
}