![](/img/trans.png)
[英]How to read a file using rememberLauncherForActivityResult in Kotlin Jetpack Compose?
[英]How to test a Jetpack Compose Composable containing rememberLauncherForActivityResult?
如何测试调用rememberLauncherForActivityResult
的 Android Jetpack Compose 可组合项?
例如:
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
import androidx.compose.runtime.Composable
import androidx.compose.material3.Button
import androidx.compose.material3.Text
@Composable
fun PickFileButton() {
val launcher = rememberLauncherForActivityResult(CreateDocument("text/plain")) {
/* do something with the returned Uri */
}
Button(onClick={launcher.launch("default_file_name.txt")}) {
Text("Pick File")
}
}
单击该按钮会启动用户用来选择文件的另一个活动。
测试看起来像:
import org.junit.Rule
import org.junit.Test
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
class TestPickFileButton {
@get:Rule val composeTestRule = createComposeRule()
@Test
fun testPickFileButton() {
composeTestRule.setContent { PickFileButton() }
composeTestRule.onNodeWithText("Pick File").performClick()
// now Android CreateDocument activity has launched
// and "composeTestRule" cannot interact with this activity
// rest of test eg to check the file was created
}
}
我无法完成测试,因为外部活动已启动,我无法使用ComposeContentTestRule
object 与此活动进行交互。
该解决方案涉及使用CompositionLocalProvider覆盖默认的ActivityResultRegistry 。 这允许您使用ActivityResultRegistry.dispatchResult发送自定义响应,而不是启动活动。
我必须阅读CreateDocument合同的源代码才能了解预期返回的Intent
类型。
这个特定问题的有效解决方案是:
import android.app.Activity
import android.content.Intent
import android.net.Uri
import androidx.activity.compose.LocalActivityResultRegistryOwner
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResultRegistry
import androidx.activity.result.ActivityResultRegistryOwner
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.core.app.ActivityOptionsCompat
import androidx.core.net.toFile
import org.junit.Rule
import org.junit.Test
@Composable
fun PickFileButton() {
val launcher =
rememberLauncherForActivityResult(CreateDocument("text/plain")) {
/* do something with the returned Uri */
}
Button(onClick = { launcher.launch("default_file_name.txt") }) { Text("Pick File") }
}
class TestPickFileButton {
@get:Rule val composeTestRule = createComposeRule()
@Test
fun testPickFileButton() {
val testUri = Uri.parse("uri_string_to_return")
composeTestRule.setContent {
// ActivityResultRegistry is responsible for handling the
// contracts and launching the activity
val registryOwner = ActivityResultRegistryOwner {
object : ActivityResultRegistry() {
override fun <I : Any?, O : Any?> onLaunch(
requestCode: Int,
contract: ActivityResultContract<I, O>,
input: I,
options: ActivityOptionsCompat?
) {
// don't launch an activity, just respond with the test Uri
val intent = Intent().setData(testUri)
this.dispatchResult(requestCode, Activity.RESULT_OK, intent)
}
}
}
CompositionLocalProvider(LocalActivityResultRegistryOwner provides registryOwner) {
// any composable inside this block will now use our mock ActivityResultRegistry
PickFileButton()
}
}
composeTestRule.onNodeWithText("Pick File").performClick()
// no activity is launched, PickFileButton will received testUri as a respose.
// rest of test eg to check the file was created
assert(testUri.toFile().exists())
}
}
资料来源: Kotlin Lang Slack Chat中的讨论, Android 存储库中的 ActivityResultRegistryTest , CompositionLocalProvider
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.