51Testing软件测试论坛
标题:
单测是如何有有序井然的进行测试的
[打印本页]
作者:
草帽路飞UU
时间:
2022-10-27 15:33
标题:
单测是如何有有序井然的进行测试的
1.1 项目中引入单测框架
单测依赖介绍如下:
// JUnit4:本地单元测试
'junit:junit:4.13.2',
'androidx.test:core:1.4.0',
// Robolectric:本地单元测试依赖 Android框架
'org.robolectric:robolectric:4.4',
// Mockito:本地单元测试模拟框架
"org.mockito:mockito-core:3.12.4",
// mock final类时出现错误:Mockito cannot mock/spy because : - final class,增加如下模拟框架
'org.mockito:mockito-inline:3.12.4',
// PowerMock:Mockito的一种扩展(以实现完成对private/static/final方法的Mock)
'org.powermock:powermock-module-junit4:2.0.9',
'org.powermock:powermock-api-mockito2:2.0.9'
比如在 app Module 的 build.gradle 的 dependencies 下,依赖 JUnit4 如下:testImplementation 'junit:junit:4.13.2'
UI 测试暂不做,但为了区分依赖,也罗列如下:
// AndroidJUnitRunner and JUnit Rules:插桩单元测试
'androidx.test:runner:1.4.0',
'androidx.test:rules:1.4.0',
// runner 和 rules 的扩展包:@RunWith(AndroidJUnit4.class) 在此扩展包的 runners 下
'androidx.test.ext:junit:1.1.3',
// Espresso:Android 界面测试
'androidx.test.espresso:espresso-core:3.4.0'
依赖 espresso 如下:androidTestImplementation ‘androidx.test.espresso:espresso-core:3.4.0'
UI 测试时需在defaultConfig中添加:testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
defaultConfig {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
UI 测试覆盖率统计开关打开:testCoverageEnabled true
android {
buildTypes {
debug {
testCoverageEnabled true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
单测可以访问编译版本的资源:includeAndroidResources = true
android {
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
1.2 编写单测代码
其实编写单测和写代码是一样的,只是使用不同工具完成功能或测试。具体操作的话,在 app/src/test/java/包名/ 下创建和代码一样的包结构,然后新建测试文件,编写单测代码即可。有如下注意事
项:
·
测试文件命名:一般是文件名加上Test后缀,比如针对 TimeUtils.kt 这个文件测试,那么测试文件可命名为TimeUtilsTest。
·
测试文件存放路径:细心的童鞋应该看到了,上一条的 TimeUtils.kt 这个文件是 kotlin 语言编写的,那么测试文件应该放在与 kotlin 相关的目录下,简单来说就是在包名和包结构中添加一层 kotlin 文
件夹,即:app/src/test/java/包名/kotlin/ 。为保持统一测试代码也建议用 kotlin 书写。
·
测试方法命名:期望输出_测试场景,如 fiveMethodsShouldBeInvoked_WhenInitData
3.4 单元测试代码分析
为了给应用开发者一个直观的印象,这里还是决定贴出一份单测代码,如有不足之处,还请海涵:
@Config(shadows = [ShadowLog::class, MockCA::class, MockDoExerciseApi::class, MockPortalApi::class], sdk = [23], application = BaseTestApplication::class)
class ProfilePresenterTest : BaseTestRobolectricClass() {
@Spy
lateinit var v: ProfileContract.V
lateinit var p: ProfilePresenter
@Before
fun setUp() {
MockitoAnnotations.openMocks(this)
p = spy(ProfilePresenter::class.java)
p.attachToView(v)
}
/**
* 命名规则:期望输出_测试场景
*/
@Test
fun fiveMethodsShouldBeInvoked_WhenInitData() {
p.initData()
verify(v).updateWeight("")
verify(v, never()).finishActivity()
verify(v, atLeastOnce()).updateHeight("")
verify(v, atLeast(1)).updateExerciseGoal("")
verify(v, times(1)).updateExerciseFrequency("")
verify(v, atMost(1)).updateExerciseTime("")
// 检查是否所有的用例都涵盖了,如果没有将测试失败。放在所有的测试后面
verifyNoMoreInteractions(v)
}
@Test
fun finishActivityMethodShouldBeInvoked_WhenResetUserInfo() {
PrivateAccessor.invoke<》rofilePresenter>(p, "resetUserInfo")
verify(v).finishActivity()
}
companion object {
private val TAG = ProfilePresenterTest::class.java.simpleName
}
}
第一行的 @Config 部分可参考 Robolectric 框架。
第二行继承了 BaseTestRobolectricClass 文件,它是作为单测代码的基类,稍后贴出源码。
@Spy 与 Mockito.spy() 方法相同,只是一个使用注解方便些。
fiveMethodsShouldBeInvoked_WhenInitData 为测试方法,verify 验证 initData 方法执行后,有5个方法会执行一次,never() 与 times() 等都是限定验证时方法的调用次数的。
最后一个方法用到了 PrivateAccessor 类,它可以通过反射的方式支持验证私有方法和属性。
BaseTestRobolectricClass 源码参考:
@RunWith(RobolectricTestRunner::class)
@Config(shadows = [ShadowLog::class], sdk = [23], application = BaseTestApplication::class)
abstract class BaseTestRobolectricClass {
protected val mContext: Context = ApplicationProvider.getApplicationContext()
companion object {
@JvmStatic
protected val TAG: String = this::class.java.simpleName
@BeforeClass
@JvmStatic
fun setup() {
ShadowLog.stream = System.out
}
}
}
2.1 统计覆盖率
在 src/test/java 上右键选择如图 Run...,会跑整体单测代码。跑完后还会在写过单测代码的文件后显示单测覆盖率。也可导出覆盖率为 HTML 文件,但不比 AS 准确。
[attach]144373[/attach]
[attach]144374[/attach]
[attach]144375[/attach]
2.2 覆盖率统计 AS 中以及导出 HTML 文件的差异
现象:AS中总代码行高于生成的HTML文件,所以显示的代码行覆盖率低于生成的HTML文件。
原因:见截图。可知,HTML文件代码行中,并未包含activity、fragment和view相关的代码行。(不知道是因为没写UI测试导致的,或是AS导HTML时导致的)
解决:目前开发时,以AS为准。
[attach]144376[/attach]
3 总结
一般来说,单测初级阶段,在统计出覆盖率后,行覆盖率达到25%或更高指标时,就算差不多了。但写单测的路也不应就此停下,在维护代码过程中会涉及对单测的修改;在后面新增功能代码时也需新增单
测代码。
欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/)
Powered by Discuz! X3.2