我有一个LiveData
变量,在收到HTTP request
的响应后,我想给它分配一个值。我在launch {}
范围内分配了该值。但是,当我测试代码以查看该值是否已实际分配时,测试失败,说明该值为null
。
我尝试了另一种选择,使用runBlocking {}
范围而不是launch {}
范围,并且我的测试用例通过了。用于联网的库为Retrofit
下面是使用launch()
并使测试用例失败的代码。
private var _state = MutableLiveData<State>()
val state: LiveData<State>
get() = _state
private var job = Job()
private val uiScope = CoroutineScope(job + Dispatchers.Main)
init {
executeRequest(....)
}
// Test cases fails with this function
private fun executeRequest(params) {
uiScope.launch {
val deferred = repository.getApi()
.requestAsync(params)
try {
val response = deferred.await()
_state.value = StateSuccess(response)
} catch (e: Exception) {
_state.value = StateError("Failure: $e.message")
}
}
}
下面是使用runBlocking()
并使测试用例通过通过的代码。
private var _state = MutableLiveData<State>()
val state: LiveData<State>
get() = _state
private var job = Job()
private val uiScope = CoroutineScope(job + Dispatchers.Main)
init {
executeRequest(....)
}
// Test cases passes with this function
private fun executeRequest(params) {
runBlocking {
val deferred = repository.getApi()
.requestAsync(params)
try {
val response = deferred.await()
_state.value = StateSuccess(response)
} catch (e: Exception) {
_state.value = StateError("Failure: $e.message")
}
}
}
这是测试用例
val viewModel = MyViewModel(fakeClientRepository)
val observer = Observer<State> {}
try {
viewModel.state.observeForever(observer)
assert(viewModel.state.value == StateSuccess("success"))
} finally {
viewModel.state.removeObserver(observer)
}
为什么launch()
没有将值分配给LiveData
变量?
答案 0 :(得分:0)
尝试使用LiveData
调度程序,然后使用postValue()
而不是value
将值分配给private fun executeRequest(params) {
uiScope.launch {
withContext(Dispatcher.IO){
val deferred = repository.getApi()
.requestAsync(params)
try {
val response = deferred.await()
_state.postValue(RESPONSE)
} catch (e: Exception) {
_state.postValue(StateError("Failure: $e.message"))
}
}
}
}
Uri uri = Uri.parse("content://com.android.externalstorage.documents/document/primary:Download");
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri);
startActivityForResult(intent, 1);
答案 1 :(得分:0)
您的函数需要成为挂起函数。这是因为您要调用的函数将在函数体内进行挂起操作,并且为此,调用函数也必须是挂起函数。暂停功能是非阻塞的,一旦触发即可进行管理,例如启动,暂停,恢复和取消。
private suspend fun executeRequest(params){
job = CoroutineScope(Dispatchers.IO).launch {
val response = repository.getApi()
.requestAsync(params).await()
withContext(Dispatchers.Main) {
try {
_state.value = StateSuccess(response)
} catch (e: Exception) {
_state.value = StateError("Failure: $e.message")
}
}
}
}
答案 2 :(得分:0)
您的测试用例在请求发出之前完成,并且LiveData
的值更改。使用runBlocking
,executeRequest
(以及您的构造函数)仅在块执行时返回;使用launch
时,它将在单独的Job
中启动它并立即返回。
如果您确实需要在构造函数中执行此操作,则可以将Job
分配给一个字段,例如
private var _state = MutableLiveData<State>()
val state: LiveData<State>
get() = _state
private var job = Job()
private val uiScope = CoroutineScope(job + Dispatchers.Main)
internal val requestJob = executeRequest(....)
private fun executeRequest(params): Job = uiScope.launch {
try {
val deferred = repository.getApi()
.requestAsync(params)
val response = deferred.await()
_state.value = StateSuccess(response)
} catch (e: Exception) {
_state.value = StateError("Failure: $e.message")
}
}
,然后在测试用例中调用requestJob.await()
,然后再检查值,例如
val viewModel = MyViewModel(fakeClientRepository)
val observer = Observer<State> {}
runBlocking {
viewModel.requestJob.await()
}
try {
viewModel.state.observeForever(observer)
assert(viewModel.state.value == StateSuccess("success"))
} finally {
viewModel.state.removeObserver(observer)
}