Юнит тест с сопрограммами и модификацией - PullRequest
0 голосов
/ 11 января 2019

Я создал приложение, используя сопрограммы и модернизацию, и оно отлично работает. Проблема возникает, когда я пытаюсь создать 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 создает проблемы, но я понятия не имею, как их решить.

EDIT

Используется следующее хранилище:

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 голосов
/ 11 января 2019

Я предполагаю, что heroesRepository.getHeroes(page) помечен как suspend , поэтому он приостановит сопрограмму и не заблокирует Main thread . Попробуйте придерживаться следующего подхода:

// 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
    }
}
...