首页 > 解决方案 > 如何使用 espresso 在 `launchFragment` 范围内测试 android 上的对话框按钮?

问题描述

问题描述

我需要测试对话框中存在的 2 个按钮是否正在执行该dismiss功能。为此,我正在使用该launchFragment函数,如文档中所述

package com.example.myapp.dialog.acr

import androidx.fragment.app.testing.launchFragment
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import com.example.myapp.R
import com.example.myapp.customViews.ACRProDialog
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class AcrProDialogTest {

    @Test
    fun closeButtonDismiss() = checkDismissOnClick(R.id.close_button)

    @Test
    fun notNowButtonDismiss() = checkDismissOnClick(R.id.not_now_button)

    private fun checkDismissOnClick(clickViewId: Int) {
        with(launchFragment<ACRProDialog>()) {
            onFragment { fragment ->
                assertThat(fragment.dialog).isNotNull()
                assertThat(fragment.requireDialog().isShowing).isTrue()
                onView(withId(clickViewId)).perform(click())
                fragment.parentFragmentManager.executePendingTransactions()
                onView(withId(clickViewId)).check(doesNotExist())
            }
        }
    }
}

问题是这样,测试需要大约 1 分钟才能运行并失败。Express显然它在通话中“卡住”了

    onView(withId(clickViewId)).perform(click())

我想这是由于范围以FragmentScenario某种方式阻止Espresso了正确执行其操作。

对话框本身很好,因为如果我在测试环境之外运行应用程序,它可以正常工作。

我第一次失败的尝试

我想简单地在FragmentScenario. 如下所示:

package com.example.myapp.dialog.acr

import androidx.fragment.app.testing.launchFragment
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.myapp.R
import com.example.myapp.customViews.ACRProDialog
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class AcrProDialogTest {

    @Test
    fun closeButtonDismiss() = checkDismissOnClick(R.id.close_button)

    @Test
    fun notNowButtonDismiss() = checkDismissOnClick(R.id.not_now_button)

    private fun checkDismissOnClick(clickViewId: Int) {
        launchFragment<ACRProDialog>()
        onView(withId(clickViewId))
            .perform(click())
            .check(doesNotExist())
    }
}

事实证明,这种方式的测试并不总是很好。dismiss()因为在检查之前对话框可能尚未运行.check(doesNotExist())。我相信这就是为什么 Android 文档建议在 a 的范围内,FragmentScenario以便我们可以强制执行函数,例如dismissusingfragment.parentFragmentManager.executePendingTransactions()

我第二次失败的尝试

我还选择了创建一个lambda先前在其范围内初始化的替代方案,该范围FragmentScenario将负责执行fragment.parentFragmentManager.executePendingTransactions(),如下所述:

package com.example.myapp.dialog.acr

import androidx.fragment.app.testing.launchFragment
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import com.example.myapp.R
import com.example.myapp.customViews.ACRProDialog
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class AcrProDialogTest {

    private lateinit var executePendingTransactions: (() -> Unit)

    @Before
    fun setUp() {
        with(launchFragment<ACRProDialog>()) {
            onFragment { fragment ->
                assertThat(fragment.dialog).isNotNull()
                assertThat(fragment.requireDialog().isShowing).isTrue()
                executePendingTransactions =
                    { fragment.parentFragmentManager.executePendingTransactions() }
            }
        }
    }

    @Test
    fun closeButtonDismiss() = checkDismissOnClick(R.id.close_button)

    @Test
    fun notNowButtonDismiss() = checkDismissOnClick(R.id.not_now_button)

    private fun checkDismissOnClick(clickViewId: Int) {
        onView(withId(clickViewId)).perform(click())
        executePendingTransactions()
        onView(withId(clickViewId)).check(doesNotExist())
    }
}

问题是这样,我得到一个IllegalStateException, 因为即使我将ragment.parentFragmentManager.executePendingTransactions()调用保留在lambda稍后将执行的 a 中,应用程序也知道该对话框与 none 无关parentFragmentManager

java.lang.IllegalStateException: Fragment ACRProDialog{8b3f628} (9ae55769-472c-4cd0-9369-8f1f7fab1b35) not associated with a fragment manager.
    at androidx.fragment.app.Fragment.getParentFragmentManager(Fragment.java:1040)
    at com.example.myapp.dialog.acr.AcrProDialogTest$setUp$1$1$1.invoke(AcrProDialogTest.kt:28)
    at com.example.myapp.dialog.acr.AcrProDialogTest$setUp$1$1$1.invoke(AcrProDialogTest.kt:28)
    at com.example.myapp.dialog.acr.AcrProDialogTest.checkDismissOnClick(AcrProDialogTest.kt:41)
    at com.example.myapp.dialog.acr.AcrProDialogTest.notNowButtonDismiss(AcrProDialogTest.kt:37)

我的对话代码

我想这不相关(因为对话框在非测试环境中正常执行其功能),但我的对话框代码在这里:

...

class ACRProDialog : DialogFragment() {

    private lateinit var binding: AcrProDialogBinding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = AcrProDialogBinding.inflate(LayoutInflater.from(context), container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.apply {
            closeButton.setOnClickListener {
                dismiss()
            }
            testProButton.setOnClickListener {
                context?.let {
                    BannerPatrocineManager(
                        context = it,
                        container = null,
                        type = BannerPatrocineManager.Type.BANNER_INTERSTITIAL,
                        originBannerInterstitial = PurchaseOrigin.ACR_POPUP
                    ).load()
                }
            }
            notNowButton.setOnClickListener {
                dismiss()
            }
        }
        dialog?.setCanceledOnTouchOutside(true)
    }

    override fun onStart() {
        super.onStart()
        (view?.parent as View).setBackgroundColor(Color.TRANSPARENT)
    }
}

标签: androidunit-testingandroid-fragmentsautomated-testsandroid-espresso

解决方案


推荐阅读