《Android编程权威指南》之音频播放与单元测试(二)

《Android编程权威指南》第 20 章第二篇,补充完 BeatBox 应用的单元测试啦。

第一篇地址:

https://juejin.cn/post/7033347707473231879

八、编写测试函数

测试函数将用到 @Test 注解。

    @Test
    fun exposesSoundNameAsTitle(){
        assertThat(subject.title,`is`(sound.name))
    }

assertThat(...) 选 org.junit 库里的 Assert.assertThat(...) 函数,is(...) 选 org.hamcrest 库里的 Is.is 函数。

上面代码意思是断定测试对象获取标题函数和 sound 的获取文件名函数返回相同的值。如果不同,单元测试失败。

接下来测试 SoundViewModel 和 BeatBox.play(Sound) 的交互。

    @Test
    fun callsBeatBoxPlayOnButtonClicked(){
        subject.onButtonClicked()
    }

为了测试 SoundViewModel 不让它跟 BeatBox 绑太死,不依赖 BeatBox 对象,就在此测试案例中模拟出 BeatBox 对象。

class SoundViewModelTest {
    ...
    private lateinit var beatBox: BeatBox
    @Before
    fun setUp() {
        beatBox = mock(BeatBox::class.java)
        ...
    }
    ...
}

Mockito 的 verify(Object) 可以确认,要测试的函数是否都按预期被调用了。

    @Test
    fun callsBeatBoxPlayOnButtonClicked(){
        ...
        verify(beatBox).play(sound)
    }
失败了

然后按照书中过程补充修正一下,把 BeatBox 传给 SoundViewModel,修正 SoundHolder 中的错误,在测试类里提供模拟板 BeatBox,实现 onButtonClicked() 函数。具体代码略了,见 Demo。

通过了

九、数据绑定回调

在布局文件里,添加数据绑定 lambda 表达式,让按钮对象和 SoundViewModel.onButtonClicked() 函数关联起来。

    

运行应用,点击按钮,就能听到奇怪的喊叫声了,可以自行体验下。

十、释放音频

音频播放完毕,应调用 SoundPool.release() 函数释放 SoundPool。

在 BeatBox 中添加方法:

    fun release(){
        soundPool.release()
    }

然后在 MainActivity 销毁的时候调用它:

    override fun onDestroy() {
        super.onDestroy()
        beatBox.release()
    }

十一、深入学习:整合测试

  • 单元测试中,受测对象是单个类。保证各个类单元正确运行,相互之间的交互符合预期。

  • 整合测试中,受测对象是应用的一部分,包括协同工作的众多对象。验证受测各部分已正确整合在一起,按预期发挥作用。在 Android 平台上,整合测试通常还是指 UI 级别的测试(和UI部件交互,验证它们的行为表现是否符合预期)。常以 instrumentation 测试来实施。

Espresso 是 Google 开发的一个 UI 测试框架,可用来测试 Android 应用。通常新项目都会自动的引用这个依赖。

 androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

可用它来测试某个 activity 的行为。

断定屏幕上某个视图显示了第一个 sample_sounds 受测文件的文件名:

@RunWith(AndroidJUnit4::class)
class MainActivityTest {
    
    @get:Rule
    val activityRule = ActivityTestRule(MainActivity::class.java)
    
    @Test
    fun showsFirstFileName(){
        onView(withText("65_cjipie"))
            .check(matches(isDisplayed()))
    }
}

@RunWith(AndroidJUnit4.class) 表明,这是一个Android工具测试,需要 activity 和其他 Android 运行时环境支持。

activityRule 上的 @get:Rule 注解告诉 JUnit,运行测试前,要启动一个 MainActivity 实例。

onView(withText("65_cjipie")) 会找到显示 “65_cjipie” 的视图,对其执行测试。

check(matches(isDisplayed())) 用来判定视图在屏幕上看得见。

有关 Espresso 详情参考:

https://developer.android.com/training/testing/espresso

十二、深入学习:模拟对象与测试

模拟对象假扮成其他不相干的组件,为的就是隔离受测对象。对于单元测试来说,能快速创建模拟对象的 Mockito 非常有用。

但是整合测试时,最好避免使用像 Mockito 这样的自动模拟测试框架,因为模拟太重了,需要很多整合测试共享,太繁琐。

基本原则:模拟对象的效用不应超出受测组件的边界。应着重关注测试范围,防止测试越界。

十三、挑战练习:播放进度控制

给 BeatBox 应用添加播放进度控制功能,在MainActivity中,使用SeekBar部件控制SoundPool的play(Int, Float, Float, Int, Int, Float)函数的播放速率参数值。

参考 Demo,指不定啥时候就更新了。O(∩_∩)O哈哈~

十四、挑战练习:设备旋转问题

给 BeatBox 应用添加一个 Jetpack 版 ViewModel,实现在设备旋转时保存BeatBox对象。

参考 Demo,指不定啥时候就更新了。O(∩_∩)O哈哈~

其他

BeatBox 项目 Demo 地址:

https://github.com/visiongem/AndroidGuideApp/tree/master/BeatBox

你可能感兴趣的:(《Android编程权威指南》之音频播放与单元测试(二))