Отображение диалога (фрагмента диалога) внутри фрагмента при тестировании пользовательского интерфейса
Я пишу тестовые примеры UI для фрагмента. Я использую launchFragmentInContainer для запуска фрагмента независимо от активности.
Сценарий: при нажатии кнопки должен отображаться фрагмент диалога, но когда я это делаю, на экране ничего не появляется.
Это мой основной фрагмент
package com.cbre.host.onboarding.locationaccess
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.navigation.fragment.findNavController
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.cbre.host.common.AppNavigator
import com.cbre.host.common.BaseFragment
import com.cbre.host.common.utils.Constants
import com.cbre.host.core.permission.PermissionProvider
import com.cbre.host.onboarding.R
import com.cbre.host.onboarding.databinding.LocationAccessFragmentBinding
import kotlinx.android.synthetic.main.location_access_fragment.*
import org.koin.android.ext.android.inject
class LocationAccessFragment : BaseFragment() {
private lateinit var binding: LocationAccessFragmentBinding
private val _viewModel: LocationAccessViewModel by fragmentViewModel()
private val _permissionProvider: PermissionProvider by inject()
private val _appNavigator: AppNavigator by inject()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = LocationAccessFragmentBinding.inflate(inflater, container, false)
binding.lifecycleOwner = viewLifecycleOwner
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// DO NOTHING
}
})
may_be_later.setOnClickListener { findNavController().navigate(R.id.next_action_a) }
access_location.setOnClickListener { handleLocationAccessPermission() }
tv_privacy_notice.setOnClickListener {
_appNavigator.openInChrome(
context!!,
Constants.PRIVACY_URL
)
}
}
override fun invalidate() = withState(_viewModel) { state ->
binding.run {
buttonOneText = state.onBoardingContent.button1Text
buttonTwoText = state.onBoardingContent.button2Text
imageHelpText = state.onBoardingContent.imageHelpText
imageText = state.onBoardingContent.imageText
imageUrl = state.onBoardingContent.imageUrl
}
}
private fun handleLocationAccessPermission() = activity?.let {
_permissionProvider.checkSelfPermission(
it,
permission = Manifest.permission.ACCESS_FINE_LOCATION,
requestPermission = { requestPermission() },
granted = {
findNavController().navigate(R.id.next_action_b)
},
shouldShowExplanation = { showDialog() })
}
private fun showDialog() {
val fm = fragmentManager
fm ?: return
val dialogFragment = LocationAccessDialogFragment.newInstance()
dialogFragment.show(fm, "locationAccessDialog")
dialogFragment.callback = object : LocationAccessDialogFragment.Callback {
override fun onAllow() {
requestPermission()
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
when (requestCode) {
PermissionProvider.REQUEST_CODE -> {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
findNavController().navigate(R.id.next_action_b)
}
}
}
}
private fun requestPermission() = requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
PermissionProvider.REQUEST_CODE
)
}
Это мой фрагмент диалога
package com.cbre.host.onboarding.locationaccess
import android.app.AlertDialog
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import androidx.fragment.app.DialogFragment
import com.cbre.host.onboarding.databinding.LocationAccessDialogBinding
class LocationAccessDialogFragment : DialogFragment() {
private lateinit var binding: LocationAccessDialogBinding
var callback: Callback? = null
interface Callback {
fun onAllow()
}
companion object {
fun newInstance() = LocationAccessDialogFragment()
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding =
LocationAccessDialogBinding.inflate(LayoutInflater.from(activity), null, false)
val builder = AlertDialog.Builder(activity)
.setView(binding.root)
val d = builder.create()
d?.window?.decorView?.setBackgroundResource(android.R.color.transparent)
binding.dontAllow.setOnClickListener {
this.dismiss()
}
binding.allow.setOnClickListener {
callback?.onAllow()
this.dismiss()
}
return d
}
}
и это мой тест
@Test
fun a_checkBtnAccessLocationClickForDenyPermission() {
val scenario = launchFragmentInContainer<LocationAccessFragment>()
scenario.onFragment { fragment ->
Navigation.setViewNavController(fragment.requireView(), navController)
}
onView(withId(R.id.access_location)).perform(click())
}
}
2 ответа
Я предполагаю, что вы хотите выполнить модульный тест для фрагмента, и в этом случае вам следует выполнить отдельный модульный тест для DialogFragment.
Модульный тест фрагмента будет выглядеть примерно так.
@Test
fun a_checkBtnAccessLocationClickForDenyPermission() {
val mockNavController = mock(NavController::class.java)
val scenario = launchFragmentInContainer<LocationAccessFragment>()
scenario.onFragment { fragment ->
Navigation.setViewNavController(fragment.requireView(), mockNavController)
}
onView(withId(R.id.access_location)).perform(ViewActions.click())
verify(mockNavController).navigate(R.id.actoin_fragment_to_locationAccessDialog)
}
}
Модульный тест диалога, диалоги не загружаются с помощью launchFragmentInContainer, поэтому вам нужно использовать launchFragment
@Test
fun positiveButton() {
launchFragment(themeResId = R.style.My_theme) {
return@launchFragment LocationAccessDialogFragment()
}
onView(allOf(withId(android.R.id.button1), withText(R.string.confirm)))
.inRoot(isDialog())
.check(matches(isDisplayed()))
.perform(ViewActions.click())
}
В вашей основной деятельности в строке изменения метода showDialog()
val fm=fragmentManager
с
val fm=childFragmentManager
Я надеюсь это поможет тебе