Skip to content

Instantly share code, notes, and snippets.

@marcellogalhardo
Last active February 5, 2021 08:37
Show Gist options
  • Save marcellogalhardo/9c111ff481e6718da3c1553f795174b9 to your computer and use it in GitHub Desktop.
Save marcellogalhardo/9c111ff481e6718da3c1553f795174b9 to your computer and use it in GitHub Desktop.
A simple scenario to run test against a view in a isolated fragment container.
/**
* Launches a [View] in an [ViewHostFragment] root view container (onCreateView).
* [ViewHostFragment] is hosted by an empty [FragmentActivity] which will [instantiate]
* the [Fragment] and waits for it to reach a resumed state.
*
* If your testing [View] has a dependency to specific theme such as [R.style.Theme_AppCompat],
* use the theme ID parameter in [launchViewInFragment] method.
*
* Usage example:
*
* ```
* launchViewInFragment { Button(context) }
* .onFragment {
* onView(instanceOf(Button::class.java)).check(matches(not(isEnabled())))
* }
* ```
*
* @param themeResId a style resource id to be set to the host [Activity]'s theme
* @param instantiate method which will be used to instantiate the [View] when
* [ViewHostFragment.onCreateView] is invoked.
*
* @see FragmentScenario for more details.
*/
fun <T : View> launchViewInFragment(
@StyleRes themeResId: Int = R.style.FragmentScenarioEmptyFragmentActivityTheme,
instantiate: ViewFactory<T>
): FragmentScenario<ViewHostFragment<T>> = launchFragmentInContainer(
themeResId = themeResId,
instantiate = { ViewHostFragment(instantiate) }
)
typealias ViewFactory<T> = (ViewBuilder) -> T
/**
* Runs the function on the main thread in a blocking way.
*
* In the lambda you have access to the view that is tested
*/
fun <T : View> FragmentScenario<ViewHostFragment<T>>.onView(action: (T) -> Unit): FragmentScenario<ViewHostFragment<T>> {
onFragment { fragment -> action(requireNotNull(fragment.viewInTest)) }
return this
}
/**
* An empty [Fragment]. This [Fragment] is used to host a [View] in [launchViewInFragment].
*
* @see [launchViewInFragment] for more details.
*/
class ViewHostFragment <T : View>(
private val viewFactory: ViewFactory<T>
) : Fragment() {
internal var viewInTest: T? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val args = ViewBuilder(requireContext(), inflater, container, savedInstanceState)
return viewFactory(args).also { viewInTest = it }
}
override fun onDestroyView() {
viewInTest = null
super.onDestroyView()
}
}
/**
* [ViewBuilder] is a generic builder of [View]'s which will be invoked
* when [ViewHostFragment.onViewCreated] is invoked.
*
* @see launchViewInFragment for more details.
*/
data class ViewBuilder internal constructor(
val context: Context,
val inflater: LayoutInflater,
val container: ViewGroup? = null,
val savedInstanceState: Bundle? = null
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment