Android Snackbar - 如何使用Roboelectric进行测试

3

这里我们现在知道robolectric没有影子对象,但是我们可以为snackbar创建一个自定义的影子对象。很遗憾他们有一个toast的影子对象,但没有snackbar的。

在我的代码中,当没有网络连接时,我会显示一个snackbar。我想知道如何编写一个单元测试(使用robolectric作为测试运行器),以验证当没有网络连接时是否会显示snackbar。

这有点困难,因为snackbar不在xml中。所以当我声明我的Activity控制器时,它此时没有snackbar。

你知道如何测试一个toast,我们有ShadowToast.getTextOfLatestToast(),我想要一个用于snackBar的。

我目前正在使用org.robolectric:robolectric:3.0-rc2,看不到可用的ShadowSnackbar.class。

3个回答

3

博客文章中实际上已经解释了如何添加ShadowToast类以启用测试。

  1. 将ShadowSnackbar添加到您的测试源代码中;
  2. 在自定义Gradle测试运行器中将Snackbar类添加为可测量类;
  3. 将ShadowSnackbar作为影子添加到您的测试中;

在应用程序代码中,当没有互联网连接时,您将调用Snackbar。由于配置(例如拦截)Snackbar作为可测量类,因此会使用该类的Shadow版本。您将能够在那时评估结果。


1
我认为这不是Robolectric的一部分,而是另一个开发者解释如何完成的,对吗? - j2emanue
2
确实,它不是Robolectric的一部分,而且很有可能不会被包含进去,除非有人贡献它。看起来如今Android及其支持库的发展速度比Robolectric更快。而那个“其他开发者”,就是我。 - Kvandermast

2

我发布了一个更加简单的答案(链接)

你可以这样做:

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

实现:

/**
 * @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
}

0

这是对我有效的方法,但它只是一个非常简单的使用案例。

@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)
        )
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接