前言
《论语·学而》中说过,吾日三省吾身
因为各种原因,换过几家公司,有外包,有自研产品,有互联网,也有物联网,令我印象深刻的还是其中一家互联网外包,负责一个德企的项目重构,代码质量规范严格,从代码规范 到 单元测试 到 提交规范,如果其中一环出现问题,代码本地都无法提交。
对于 代码规范 可以参考阿里的开发规范手册
对于 Git提交规范 可以使用git hook定制不同提交时的校验
对于单元测试来而言,可以说是整个环节中最重要的部分了,应该是每个开发同学掌握的必修技能,不仅能保证项目的健壮性,也能减少和测试同学的矛盾,进而保证自己的KPI~
Android中的测试分类
在一个完整的Android项目中,需要测试的点有很多
根据测试类型分类:
- 功能测试 : 业务逻辑是否按照我需要的方式执行,主要手段通过单元测试
- 性能测试 : 某个方法的执行效率,多线程阻塞等
- 兼容性测试 : 应用对设备的最大最小兼容性,适配性
根据测试范围分类:
- 单元测试 或小测试仅验证应用的一小部分,例如方法或类。
- 中型测试介于两个单元之间,用于检查两个或多个单元之间的集成。
- 业务交互测试,从UI发送事件,到最终的正确业务流
下面内容会从单元测试出发,介绍一下如何在Anroid中的正确的使用单元测试来维护代码健壮性,稳定性。
Android中的单元测试
单元测试概述
- 定义: 单元测试是针对应用中的最小可测试部分(通常是一个方法或类)的自动化测试。
- 重要性: 确保代码质量,减少bug,提供快速反馈,促进代码重构。
Android中的单元测试框架
- JUnit 基于Java的单元测试框架,广泛使用于Java开发中。
- AndroidJUnit4 基于基于Android开发单元测试框架,广泛使用于Android开发中。
- Mockito: 一个流行的Java模拟框架,用于模拟依赖和测试交互。
- Robolectric: 允许在JVM上运行Android单元测试,提供对Android API的模拟。
默认情况下,本地单元测试的源文件位于 module-name/src/test/ 中。当使用 Android Studio 创建新项目时,此目录会自动创建。
androidTest
目录应包含在真实或虚拟设备上运行的测试。此类测试包括集成测试、端到端测试,以及仅靠 JVM 无法验证应用功能的其他测试。test
目录应包含在本地计算机上运行的测试,如单元测试。与上述方法不同,这些测试可以是在本地 JVM 上运行的测试。
Junit单元测试的编写
依赖库版本
arduino
testImplementation 'junit:junit:4.14-SNAPSHOT'
一般情况下,新项目创建时,会默认依赖
编写方法用例 @Test
通过注释 @Test方法,标记该方法为Test方法,用于测试另外一个方法
assertEquals断言
用于判断两个值是否相等
参数一、期待值
参数二、真实值
测试方法命名规范:
一般情况下,使用should<动作>When<条件>
的格式命名,例如:should_return_sum_when_two_number_add 或者像图片里面
运行单元测试
点击方法前面的绿色三角,就会运行测试方法(无需运行android虚拟机)
断言结果如果成功 如图所示 失败 在控制台输出期待值 和 真实得结果值,并在代码区域高亮提示
方法执行前@Before
通常情况下sum() 方法并不会在测试类中,我们想测试某个类中的方法,就需要去创建一个类的实例,可以直接在@Test方法里面去创建,但是当一个类中测试方法过多的时候,每个方法都需要手动创建实例,就会很繁琐,所以可以通过@Before,在方法调用前,去初始化一些示例 和配置
计算器类
KOTLIN
public class Calculator {
public int addition(int number, int numberTwo) {
return number + numberTwo;
}
public int subtraction(int number, int numberTwo) {
return number - numberTwo;
}
}
单元测试
KOTLIN
class ExampleUnitTest {
private lateinit var calculator: Calculator
//预置Calculator实例
@Before
fun setUp() {
calculator = Calculator()
}
@Test
fun shouldReturnSumWhenTwoNumbersAreAdded() {
val result: Int = calculator.addition(2, 3)
println("addition result $result")
assertEquals(5, result)
}
@Test
fun shouldReturnSubWhenTwoNumbersAreSubtraction() {
val result: Int = calculator.subtraction(3, 2)
println("subtraction result $result")
assertEquals(1, result)
}
}
其它常见注解
- @After既然有方法执行前的动作,就有@After,使用方式和Before一样,功能一般情况下用于重置对象里面的一些属性。
- @BeforeClass 用于标记一个方法在所有测试方法之前运行,并且只运行一次。通常用于一次性的初始化工作。
- @AfterClass 用于标记一个方法在所有测试方法之后运行,并且只运行一次。通常用于一次性的清理工作。
为一个类新建单元测试
- 1、在需要测试的类ctrl+shift+T
- 2、选择Create NewTest
- 可以选择Junit版本,一般项目自带的都是4,
- 测试类名
- 测试类的父类
- 目标包名
- 下面一些默认的注解,上面有写
- 下面是需要为哪些方法生成测试方法
配置完毕后,点击ok就会自动生成了
运行器
可以通过注解 @RunWith(xx.class) 用于指定当前测试类使用xxx的运行器来运行测试。
BlockJUnit4ClassRunner::class
不需要显式指定,JUnit 4的默使用该运行期。
按照类中定义的顺序执行测试方法。
Parameterized::class
用于参数化测试。
允许你使用不同的参数运行同一个测试方法多次
假设我们想对加法 进行多次验证,每次运行都需要手动去修改参数和结果,这样效率会很低下,这时候就可以使用 @Parameterized运行器,来替我们初始化参数
kotlin
@RunWith(Parameterized::class)
class ParameterizedTest(private val a: Int, private val b: Int, private val expected: Int) {
companion object {
@JvmStatic
@Parameterized.Parameters
fun data(): Collection<Array<Int>> {
return listOf(
arrayOf(1, 2, 3),
arrayOf(2, 3, 5),
arrayOf(3, 4, 8)
)
}
}
private lateinit var calculator: Calculator
@Before
fun setUp() {
calculator = Calculator()
}
@Test
fun shouldReturnSumWhenTwoNumbersAreAdded() {
val result: Int = calculator.addition(a, b)
println("addition result $result")
assertEquals(expected, result)
}
}
当我们运行shouldReturnSumWhenTwoNumbersAreAdded()方法时,系统会根据预置参数 调用三次,并按照array里面的数据进行依次测试。
Suite::class
通过一个类收集并运行多个测试类。
通过上面,我们已经生成多个测试类,当想对测试类里面所有的方法进行验证时,我们需要单独运行测试类上面的按钮,如果想同时对几个测试类做验证时,我们可以通过Suite::class,来整合所有的测试类,进行一次执行
kotlin
@RunWith(Suite::class)
@Suite.SuiteClasses(
ExampleUnitTest::class, ParameterizedTest::class
)
class TestSuite
执行结果
其它运行器
除去上面基本的一些运行器以外,还会有一些其他进阶的运行器,例如MockIto,AndroidJunit4,我会在下一篇文章进行详细的讲解~