【玩转Test】Fragment 集成测试,FragmentScenario Espresso Mockito介绍

2,994 阅读3分钟

系列文章

前言

前三篇文章我们介绍了如何写单元测试,从这篇文章开始,我们介绍一下 集成测试

fragment 和 ViewModel 联系很紧密,我们需要确保 ViewModel 在适当时的时机更新 UI,那么该如何测试这部分内容呢?


本文内容来自 Udacity Advanced Android with Kotlin-Lesson 11-5.2 Testing: Intro to Test Doubles & Dependency Injection

Fragment 集成测试

为了在下面的架构上进行 集成测试 ,我们需要尽可能的屏蔽无关代码

例如我们可以使用 empty activity,它不包含 fragment 或 activity 的其他代码。对于数据层,可以使用 test doubles 来替代

这样就可以聚焦于 fragment 和 ViewModel 的代码

FragmentScenario

当你需要测试 activity 和 fragment 时,AndroidX test 中的 FragmentScenarioActivityScenario 的 API 可以帮到你

引入

debugImplementation "androidx.fragment:fragment-testing:$fragmentVersion"
implementation "androidx.test:core:$androidXTestCoreVersion"

使用

这些 API 用于为测试提供 fragment 和 activity ,你可以控制它们的启动情况和生命周期状态

以下代码用于启动 fragment 并传入 bundle

val bundle = Bundle().apply { putString("username", "Flywith24") }
val scenario = launchFragmentInContainer<RepoListFragment>(bundle, R.style.AppTheme)

如果想要控制 fragment 的生命周期状态,可以调用

scenario.moveToState(Lifecycle.State.CREATED)

由于 FragmentScenario 是 AndroidX test 一部分,因此在 local test 和 instrumented test 中均可使用

Espresso

如果想要测试 Android 中的 UI 组件可以使用 Espresso 库,使用该库你可以使用 view 并检查它们的状态

引入

androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"

使用

一个 Espresso 有四个最基本的部分

Espresso 介绍
Espresso 介绍

onView()Espresso 中一个常用的静态方法,它意味着接下来要对 view 进行操作

ViewMatches 的职责就是寻找 view ,上图中使用的是 withId() 方法,根据 id 匹配相应的 view。还有一些其他的匹配方法,例如 withText

注意:保证 ViewMatches 只能匹配到一个 view,否则会抛出 AmbiquousviewMatcher Exception

ViewAction 是 view 执行的动作,示例中是 click 方法

通过 ViewAssertion 我们可以判断 view 的状态是否符合我们的预期

tips:为了提高响应速度,您可以在开发者选项中将动画关闭

关闭动画
关闭动画

Mockito

Mock

我们在 【玩转 Test】Test Doubles 的概念及如何测试 Repository 中介绍了 test doubles 的类型,其中我们主要介绍了 Fake。今天,我们来介绍 test doubles 的另一种类型:Mock

不同于 FakeMock 侧重于跟踪方法的调用,这么说可能比较抽象,让我们举个栗子

例子
例子

如上图,有一个方法被调用并改变了 UI(更新 text )

如果使用 Mock ,则验证更新 text 的方法是否被正确地调用

如果不使用 Mock,我们通常会验证 TextView 中的 text 是否符合预期

引入 Mockito

为了进行 Mock 测试,我们需要引入 Mockito

androidTestImplementation "org.mockito:mockito-core:2.25.0"
androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito:2.12.1"

有的小伙伴可能会问了,有什么场景需要使用 Mock 吗?

这里有一个例子比较合适,测试使用 navigation 进行 fragment 的跳转

Testing Navigation

我们有一个 HostFragment ,内部有一个 button,点击可以跳转到 RepoListFragment 并将用户名传递过去

navigation
navigation

接下来我们来测试这部分的跳转

首先提供第一个 fragment

// GIVEN 显示 fragment
val scenario = launchFragmentInContainer<HostFragment>(Bundle(), R.style.AppTheme)

由于我们使用了 navigation ,因此我们还需要 navigationController

val navController = Mockito.mock(NavController::class.java)
scenario.onFragment {
  Navigation.setViewNavController(it.view!!, navController)
}

最后我们执行点击按钮动作并验证 navigation 的跳转和参数传递是否符合要求

// WHEN 点击搜索按钮
onView(withId(R.id.button)).perform(click())

// THEN 验证跳转到 repolist 界面
verify(navController).navigate(HostFragmentDirections.actionHostFragmentToRepoListFragment("Flywith24"))

关于我

我是 Fly_with24