赞
踩
Android的测试的组件比较多,像JUnit,Espresso,UiAutomator,Mockito
Android的测试范围也包括很多种,比如Java代码测试,Android逻辑测试,AndroidUI测试
每个框架的侧重点各有不同,这里我们专门来介绍前三个Google官方推荐的测试框架
有些测试组件是只在AndroidTest中可用的,在JavaTest目录下如果访问不了,不用大惊小怪
testApplicationId "com.android.unit.test" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testOptions { unitTests { includeAndroidResources = true } } useLibrary "android.test.runner" useLibrary "android.test.base" useLibrary "android.test.mock" implementation "androidx.annotation:annotation:+" testImplementation "junit:junit:+" androidTestImplementation "androidx.test:core:+" androidTestImplementation "androidx.test:core-ktx:+" androidTestImplementation "androidx.test.ext:junit:+" androidTestImplementation "androidx.test.ext:junit-ktx:+" androidTestImplementation 'androidx.test:runner:+' androidTestImplementation 'androidx.test:rules:+' androidTestImplementation "androidx.test.espresso:espresso-core:+" androidTestImplementation "androidx.test.uiautomator:uiautomator:+" androidTestImplementation "org.hamcrest:hamcrest-integration:+" androidTestImplementation "com.google.truth:truth:+"
@RunWith指定这是一个单元测试程序,并指定测试组件
@Test指定要进行测试的函数
@Before和@After指定@Test前后的准备和清理工作
@BeforeClass和@AfterClass指定进程启动结束时的准备和清理工作
assert用来判断结果是否符合预期,所有assert全部通过代表该单元测试用例通过
点击方法左侧的Run按钮,可以测试当前用例
点击类名左侧的Run按钮,可以测试所有用例
点击Run with Coverage,可以测试并统计通过率
import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @RunWith(JUnit4::class) class BasicTest { var arg1 = 1 var arg2 = 2 var result = 3 @Test fun test() { Assert.assertEquals(arg1 + arg2, result) } }
Parameterized组件允许提供多组测试数据,对测试用例进行测试
测试数据通过@Parameterized.Parameters标注的静态方法来创建
import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @RunWith(Parameterized::class) class ParameterizedTest { @JvmField @Parameterized.Parameter(0) var arg1 = 0 @JvmField @Parameterized.Parameter(1) var arg2 = 0 @JvmField @Parameterized.Parameter(2) var result = 0 // offer groups of test data companion object { @JvmStatic @Parameterized.Parameters fun data(): Iterable<Array<Any>> { return listOf( arrayOf(0, 0, 0), arrayOf(1, 1, 2), arrayOf(3, 2, 5), arrayOf(4, 3, 7) ) } } @Test @Throws(InterruptedException::class) fun add() { Assert.assertEquals(arg1 + arg2, result) } }
由于单元测试是不经过Application启动流程的
如果我们想通过Context去启动Activity,或者获取某些权限的话,就无法直接获取了
此时我们可以通过androidx.test组件提供的InstrumentationRegistry和GrantPermissionRule类来实现
import android.Manifest import android.os.Environment import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import java.io.File import java.io.FileOutputStream @RunWith(AndroidJUnit4::class) class AndroidTest { val appContext = InstrumentationRegistry.getInstrumentation().targetContext @get:Rule var rule: GrantPermissionRule = GrantPermissionRule.grant( Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE ) @Test fun createFile() { val dir = appContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES) val file = File(dir, "x.png") val fos = FileOutputStream(file) fos.use { it.write(byteArrayOf(1, 2, 3, 4, 5)) } val length = file.length() System.out.println(length) } }
AndroidTest在执行完测试方法后,就会立刻退出测试进程
所以,AndroidTest只适合做一些基本的逻辑验证和UI验证工作,不适合长期运行Activity
如果一定要在AndroidTest中持续运行Activity的话,也有个投机取巧的方法
那就是让目标Activity运行在独立进程,启动Activity后让测试方法一直sleep,这样测试进程就不会退出
但是一般不建议这么去做,AndroidTest本身是很耗性能的
如果长期运行UI,再添加断点调试的话,时常会发生应用阻塞和退出的情况,达不到预期的效果
Instrument Test指的是仪器测试,在真机上进行测试的意思
在Android上,仪器测试主要是指UI测试,最常用的框架是Espresso和UiAutomator
Espresso可以模拟界面输入点击,验证处理结果是否符合预期
下面以一个简单的加法计算器,来演示Espresso的使用方法
测试流程为,向a输入框和b输入框输入数字,点击按钮,将a和b相加,结果存入c输入框
判断成功的标准为,获取c输入框的值,和指定值相等
package com.android.code import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.android.code.databinding.ActivityHomeBinding class HomeActivity : AppCompatActivity() { private lateinit var binding: ActivityHomeBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityHomeBinding.inflate(layoutInflater) setContentView(binding.root) addEvents() } private fun addEvents() { binding.button.setOnClickListener { val a = binding.aEdit.text.toString().toInt() val b = binding.bEdit.text.toString().toInt() val c = (a + b).toString() binding.cEdit.setText(c) } } }
Espresso的核心类主要有三个:ViewMatchers,ViewActions,ViewAssertions
分别用来查找符合条件的View,对View进行操作,判断View的属性是否符合预期
import android.app.Application import androidx.lifecycle.Lifecycle import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.code.HomeActivity import com.android.code.R import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class InstrumentTest { private lateinit var scenario: ActivityScenario<HomeActivity> @Before fun launchActivity() { scenario = ActivityScenario.launch(HomeActivity::class.java) } @Test fun sumEditText() { // get context val context = ApplicationProvider.getApplicationContext<Application>() // listener activity launch scenario.onActivity { activity -> assertEquals(activity.lifecycle.currentState, Lifecycle.State.RESUMED) activity.window.decorView.background = context.resources.getDrawable(R.drawable.ic) } // input a Espresso.onView( ViewMatchers.withId(R.id.aEdit) ).perform( ViewActions.typeText("1"), ViewActions.closeSoftKeyboard() ) // input b Espresso.onView( ViewMatchers.withId(R.id.bEdit) ).perform( ViewActions.typeText("2"), ViewActions.closeSoftKeyboard() ) // sum a and b Espresso.onView( ViewMatchers.withId(R.id.button) ).perform( ViewActions.click() ) // assert c Espresso.onView( ViewMatchers.withId(R.id.cEdit) ).check( ViewAssertions.matches( ViewMatchers.withText("3") ) ) // prevent test thread from exit Thread.sleep(100 * 1000L) } }
UiAutomator和Espresso定位有所区别
Espresso是白盒测试,可以直接控制应用内的操作行为
UiAutomator是黑盒测试,站在设备的角度,对所有APP进行调度,但无法控制APP的内部逻辑
这里同样以加法计算器的例子来演示UiAutomator的用法
UiAutomator的三个核心类是UiDevice,UiObject,UiSelector
UiDevice表示安卓手机设备
UiObject表示控件元素
UiSelector表示控件元素的查找条件
UiAutomator没有提供专门的Assertion类,通过JUnit或GoogleTruth等提供的方法来断言都可
import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiSelector import com.android.code.HomeActivity import com.google.common.truth.Truth import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class InstrumentTest { private lateinit var scenario: ActivityScenario<HomeActivity> private lateinit var uiDevice: UiDevice @Before fun launchActivity() { scenario = ActivityScenario.launch(HomeActivity::class.java) uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) } @Test fun sumEditText() { // direct to message app uiDevice.pressHome() val messageApp = uiDevice.findObject( UiSelector().text("Messages") ) messageApp.clickAndWaitForNewWindow() // return to code app uiDevice.pressHome() val codeApp = uiDevice.findObject( UiSelector().text("Code") ) codeApp.clickAndWaitForNewWindow() // input a val aEdit = uiDevice.findObject( UiSelector().resourceId("com.android.code:id/aEdit") ) aEdit.setText("1") // input b val bEdit = uiDevice.findObject( UiSelector().resourceId("com.android.code:id/bEdit") ) bEdit.setText("2") // click sum button val button = uiDevice.findObject( UiSelector().resourceId("com.android.code:id/button") ) button.click() // assert c by google-truth val cEdit = uiDevice.findObject( UiSelector().resourceId("com.android.code:id/cEdit") ) Truth.assertThat(cEdit.text).isEqualTo("3") // prevent test thread from exit Thread.sleep(3 * 1000L) } }
OK,安卓常用的测试套件基本就这些了
还有很多第三方的测试套件,原理基本都差不多
更细的使用技巧,就需要大家在实践中去掌握了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。