协程和改造的单元测试

时间:2019-01-11 09:42:28

标签: unit-testing kotlin coroutine kotlinx.coroutines

我使用协程和改造创建了一个应用程序,并且运行良好。当我尝试为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)
        }
    }
}

1 个答案:

答案 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
    }
}
相关问题