文章思路
- 为什么要进行单元测试
- 提高稳定性,明确是否正确完成开发
- 快速反馈bug ,跑一遍单元测试用例,定位 bug
- 最小化技术债偿还,越往后修复 bug 的代价会越大
- 为代码重构提供安全保障,优化代码不用担心回归问题
- 单元测试要测什么
- 列出想要测试覆盖的正常、异常情况,进行测试验证
- 性能测试,例如算法的耗时等
- android 单元测试分为本地测试和 Instrumented 测试
- 本地测试,
路径: module-name/src/test/java
特点:无需设备支持,速度快。不可测试包含android 系统API 代码
- Instrumented
路径:module-name/src/androidTest/java
特点:需运行设备,可使用android 系统API
- 项目代码分类
1. 强依赖组件关系,如 Activity、Service 等组件,与生命周期相关,无法进行单元测试,可以进行 Espresso 等UI 测试
2. 部分依赖,代码实现依赖注入,可使用 Mock 进行模拟 Context 等 android 对象的依赖注入
3. 纯Java 代码,不存在对 android 库的依赖,可以进行 JUnit 单元测试
- 常用测试框架及选择
单元测试
├── 是否依赖 android 库
├── 否 ->
├── 是否需要 mock
├── 否 -> JUnit
├── 是 -> JUnit + Mock
├── 是
├── 是否可用 mock
├── 是 -> JUnit + Mock
├── 否
├── AndroidJunitRunner + Mock
- 测试框架例子
Android 单元测试实例代码
## JUnit4 单元测试
1. CalculatorTest 单元测试例子
2. CalculatorWithParameterTest ,多个参数覆盖测试
3. UnitTestSuite ,运行多个单元测试的测试集合
## AndroidJunit 单元测试
1. SharedPreferencesHelperTest。 带Mock 的测试 android api 例子
- Mockito 实现原理,涉及 cglib 库
- Mockito 本质是个 Proxy 代理模式的应用
- Proxy 代理,是给对象提供一个 Proxy 对象,所有对真实对象的调用,都会先经过 proxy 对象,然后 proxy 对象确定自己处理还是调用真实对象来处理
- Mockito 本质上是代理对象调用方法前,用 stub 方式设置其返回值,然后在真实调用时,用代理对象返回预设的返回值
常用测试框架实例
1. JUnit4 简单测试例子
class CalculatorTest {
private lateinit var mCalculator: Calculator
@Before
fun setUp() {
mCalculator = Calculator()
}
@Test
fun addTwoNumbers() {
var addTwoNumbers = mCalculator.addTwoNumbers(1, 2)
assert(addTwoNumbers == 3) {
"addTwoNumbers is error"
}
}
}
2. JUnit4 多个参数
@RunWith(Parameterized::class)
class CalculatorWithParameterTest(val addA: Int, val addB: Int, val exceptV: Int) {
companion object {
@JvmStatic
@Parameterized.Parameters
fun data(): Collection<Array<Any>> {
return Arrays.asList(
arrayOf<Any>(0, 0, 0),
arrayOf<Any>(0, -1, -1),
arrayOf<Any>(2, 2, 5),
arrayOf<Any>(8, 8, 16),
arrayOf<Any>(16, 16, 33),
arrayOf<Any>(32, 0, 32),
arrayOf<Any>(64, 64, 129)
)
}
}
private lateinit var mCalculator: Calculator
@Before
fun setUp() {
mCalculator = Calculator()
}
@Test
fun addTwoNumbers2() {
var addTwoNumbers = mCalculator.addTwoNumbers(addA, addB)
assert(addTwoNumbers == exceptV) {
"addTwoNumbers is error"
}
}
}
3. JUnit4 一次性执行多个测试用例
@RunWith(Suite::class)
@Suite.SuiteClasses(CalculatorTest::class, CalculatorWithParameterTest::class)
class UnitTestSuite
4. AndroidJunit+ Mock 测试SP 存读
@RunWith(AndroidJUnit4::class)
class SharedPreferencesHelperTest {
lateinit var mContext: Context
lateinit var mSharePreferences: SharedPreferences
lateinit var sharePreferenceHelper: SharePreferenceHelper
@Before
fun setUp() {
mContext = InstrumentationRegistry.getTargetContext()
mSharePreferences = PreferenceManager.getDefaultSharedPreferences(mContext)
var mockSharedPref = Mockito.mock(SharedPreferences::class.java)
var mockEditor = Mockito.mock(SharedPreferences.Editor::class.java)
org.mockito.Mockito.`when`(mockSharedPref.edit()).thenReturn(mockEditor)
`when`(mockEditor.commit()).thenReturn(true)
`when`(mockSharedPref.getString(SharePreferenceHelper.KEY_NAME, "")).thenReturn("myname")
`when`(mockSharedPref.getString(Matchers.anyString(), "")).thenReturn("myname")
sharePreferenceHelper = SharePreferenceHelper(mSharePreferences)
}
@Test
public fun testSave() {
var saveBean = sharePreferenceHelper.saveBean(KeyBean("1", "12"))
assert(saveBean)
}
@Test
public fun testPut() {
var bean = sharePreferenceHelper.getBean()
assert(bean.name == "myname")
}
}
链接
- github 代码: https://github.com/haichaoyuan/unitTestStudy
- 参考: https://www.jianshu.com/p/925191464389