简体   繁体   中英

android snackbar - how to test with roboelectric

From here we now know that robolectric does not have a shadow object but we can create a custom shadow object for a snackbar.It's ashame they have one for toast but not for snackbar.

I am showing a snackbar in my code when there is no.network connection. I'd like to know how can i write a unit test (with robolectric as the test runner) that can verify that a snackbar gets shown when there is no.network connection.

Its a little hard because the snackbar is not in xml. So when i declare my actually Activity controller it doesn't have a snackbar at that time.

You know how to test a toast we have ShadowToast.getTextOfLatestToast() i want one for snackBar

im currently using org.robolectric:robolectric:3.0-rc2 and dont see ShadowSnackbar.class available.

It's actually explained in the blogpost how to add the ShadowToast class to enable testing.

  1. Add the ShadowSnackbar to your test sources;
  2. Add the Snackbar class as an instrumented class in your custom Gradle test runner;
  3. Add the ShadowSnackbar as a Shadow in your test;

In your app's code, you'll call on the Snackbar when no internet connection is available. Because of the configuration (eg intercepting of) the Snackbar as Instrumented class, the Shadow-variant of the class will be used. You'll be able to evaluate the result at that moment.

I posted a much simpler answer

you can just do:

 val textView: TextView? = rootView.findSnackbarTextView() assertThat(textView, `is`(notNullValue())) 

Implementation:

/**
 * @return a TextView if a snackbar is shown anywhere in the view hierarchy.
 *
 * NOTE: calling Snackbar.make() does not create a snackbar. Only calling #show() will create it.
 *
 * If the textView is not-null you can check its text.
 */
fun View.findSnackbarTextView(): TextView? {
  val possibleSnackbarContentLayout = findSnackbarLayout()?.getChildAt(0) as? SnackbarContentLayout
  return possibleSnackbarContentLayout
      ?.getChildAt(0) as? TextView
}

private fun View.findSnackbarLayout(): Snackbar.SnackbarLayout? {
  when (this) {
    is Snackbar.SnackbarLayout -> return this
    !is ViewGroup -> return null
  }
  // otherwise traverse the children

  // the compiler needs an explicit assert that `this` is an instance of ViewGroup
  this as ViewGroup

  (0 until childCount).forEach { i ->
    val possibleSnackbarLayout = getChildAt(i).findSnackbarLayout()
    if (possibleSnackbarLayout != null) return possibleSnackbarLayout
  }
  return null
}

this is what worked for me, but it was a very simple use case

@Implements(Snackbar::class)
class CustomShadowSnackbar {

    companion object {
        val shownSnackbars = mutableListOf<Snackbar>()

        fun Snackbar.getTextMessage(): String {

            val view = (this.view as ViewGroup)
                .children
                .first { it is SnackbarContentLayout } as SnackbarContentLayout

            return view.messageView.text.toString()
        }

        fun clear() {
            shownSnackbars.clear()
        }
    }

    @RealObject
    lateinit var snackbar: Snackbar

    @Implementation
    fun show() {
        shownSnackbars.add(snackbar)
    }

    @Implementation
    fun __constructor__(
        context: Context,
        parent: ViewGroup,
        content: View,
        contentViewCallback: ContentViewCallback) {

        Shadow.invokeConstructor(
            Snackbar::class.java,
            snackbar,
            ReflectionHelpers.ClassParameter(Context::class.java, context),
            ReflectionHelpers.ClassParameter(ViewGroup::class.java, parent),
            ReflectionHelpers.ClassParameter(View::class.java, content),
            ReflectionHelpers.ClassParameter(ContentViewCallback::class.java, contentViewCallback)
        )
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM