簡體   English   中英

如何測試包含 rememberLauncherForActivityResult 的 Jetpack Compose Composable?

[英]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 存儲庫中的 ActivityResultRegistryTestCompositionLocalProvider

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM