Android Test1 介绍和引入

Android UnitTest 介绍和引入

后续会写几篇与 Android 测试有关的文章。之所以准备写,是因为在国内做 Android 开发的程序员很少在开发过程中,在写完功能程序的前提下,自己写用例程序来测试自己的程序,主要还是依靠测试人员来测试(国内的测试人员,绝大多数做的是不符合软件测试程序专业程序的)。而恰恰一些功能的边界测试或者特殊场景测试,一般测试人员是无法测试到的,只有通过造数据的方式才能测试到。

下面就看下 Android 中的一些测试的基本信息。



Android 测试类型

按功能测试分

  • 功能测试:功能是否按照预期执行。
  • (无障碍)性能测试:功能是否高效执行完成。
  • 兼容性测试:功能是否能够在所有设备和 API 级别上正常运行。

按照测试范围分能

  • 单元测试/小型测试:测试一个类或方法。
  • 端到端测试/大型测试:测试整个屏幕或者用户体验流程。
  • 中型测试:介于上述两者之间,用于检查两个或更多单元之间的集成

上述两种分类方式是 Android 官网上的分类方式。

对开发者来讲,从 Android 工程结构上看,是将代码写在 src/test 目录中还是 src/androidTest 目录中。这样可以分成 单元测试界面测试

单元测试代码只是基于 JVM 的代码测试,可以不运行到模拟器或真机环境中。

界面测试,需要测试代码运行到模拟器或真机上。

另外两个概念是:

  • 小型本地测试:可以使用本地运行的 Android 模拟器(基于 JVM 模拟 Android 环境),例如 Robolectric
  • 小型插桩测试:您可以验证代码能否很好地支持框架功能(例如 SQLite 数据库)。

下面开始分析我在项目中的基本情况。



软件版本

这篇文章中列举的是我在当前项目中使用的相关环境及软件版本,后续可能会随着项目的发展对软件版本做出修改。

首先来说明下当前项目的环境及 IDE 版本等信息。

工具版本说明
Ubuntu24.04.2 LTS稳定版本
Android Studio Meerkat Feature Drop2024.3.2 Patch 1最新稳定版
AGP (Android Gradle Plugin)7.4.2
Gradle7.6.4当前项目可使用的最高版本,受到其他必须使用的库的限制。
KGP (Kotlin Gradle Plugin)1.9.0
Kotlin1.9.0
JDK17
junit4.13.2
robolectric4.14.1结合 AGP 7.4.2 运行,可运行的版本有 4.14.1,4.13,4.12.2,4.12.1,4.11,4.10.3。

引入 JUnit4 和 Robolectric

在写单元测试前,先按测试需要配置引入 JUnit4 和 Roboletric 库。

// Gradle
dependencies {
    // Test libs 基本库
    testImplementation "androidx.test:core:1.5.0" // AndroidX Test library
    testImplementation "androidx.test.ext:junit-ktx:1.2.1"
    testImplementation "junit:junit:4.13.2"  // JUnit4 的最新稳定版
    testImplementation "org.robolectric:robolectric:4.14.1"  // 最新稳定版
}

这里配置的是 JUnit4 而非 JUnit5,主要在于 Android 官方目前在介绍使用 Robolectric 时使用的是 JUnit4 来作为最佳实践,即 robolectric 底层的基础实现是 JUnit4,与 JUnit5 的兼容性目前不得而知。

下面看下 JUnit5 和 JUnit4 的部分介绍。


JUnit5 和 JUnit4

JUnit5 到现在,版本已经发展到 5.13.0 版本,而 JUnit4 已经在维护阶段,最后的 JUnit4 版本是 4.13.2。

JUnit5 是当前最新测试框架。它基于 JVM,聚焦在 Java 8 及以上版本,且在实现的设计上更加灵活。相比于 JUnit4,JUnit5 提供了更加强大的功能和更好的扩展性,当前它也是主流。

下面来看下两者在 Android 测试中特性。

特性JUnit4JUnit5
参数化测试需要依赖外部库(如 Theroies)原生支持
动态测试不支持原生支持
扩展模式有限强大且灵活
Java 版本支持Java 7+Java 8+
Android 兼容性完全兼容单元测试兼容,界面测试需 JUnit4

项目一开始准备的是 JUnit5 和 JUnit4 混用,但是遇上问题。


JUnit5 和 JUnit4 混用问题

sdk/build.gradle 文件中做了 JUnit5 和 JUnit4 的配置。

dependencies {
  // .....

  // Test libs
  testImplementation "androidx.test:core:1.5.0" // AndroidX 测试库
  testImplementation "androidx.test.ext:junit-ktx:1.2.1"

  // JUnit5 API
  testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.3'
  // JUnit Jupiter 引擎(运行 JUnit5 测试)
  testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.3")

  testImplementation "junit:junit:4.13.2"
  // JUnit Vintage 引擎(运行 JUnit4 测试)
  testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.9.3")
  
  testImplementation "org.robolectric:robolectric:4.14.1"
}

Junit5 不再是单个模块就可以解决问题的了,它分成 Platform, Jupiter, Vintage 3 个模块。

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

配置文件中简单写了作用,各模块更详细的作用可以点击 JUnit5 User Guide 了解更多。

由于测试的方法中有依赖 android.content.Context 类,因此还引入了 robolectric 库

第一个问题:运行任务 sdk:testDebugTestUnit 一直执行,不返回任何结果。

出现这个问题时,运行的测试代码如下。

@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.R])
class ContextTest {

    private lateinit var _context: Context

    @Before
    fun init() {
        val application = ApplicationProvider.getApplicationContext<Context>()
        println("init(): Application=$application")
        _context = application.applicationContext
    }

    @Test
    fun test_getEntropyPool_shouldReturnRespGetEntropyPool() {
        val deviceId =  "9ijuyh7wolkirwfs"
        println("test_getEntropyPool_shouldReturnRespGetEntropyPool(): $deviceId")
        Assert.assertNotNull(deviceId)
    }
}

这个问题可能原因中分析是 JUnit4,JUnit5 混用,导致 test runner 启动时无法正确启动。


第二个问题:错误提示 No instrumentation registered! Must run under a registering instrumentation.

运行的测试代码

@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.R])
class ContextTest {

  private lateinit var _context: Context

  companion object {
    
    @BeforeClass
    @JvmStatic
    fun init() {
        val application = ApplicationProvider.getApplicationContext<Context>()
        println("init(): Application=$application")
        _context = application.applicationContext
    }
  }
  
  @Test
  fun test_getEntropyPool_shouldReturnRespGetEntropyPool() {
      val deviceId =  "9ijuyh7wolkirwfs"
      println("test_getEntropyPool_shouldReturnRespGetEntropyPool(): $deviceId")
      Assert.assertNotNull(deviceId)
  }
}

这个问题原因标注 @BeforeClass 的方法会在所有 @Test 方法执行之前先执行,但这时 Robolectric 还没有初始化完成,ApplicationProvider 等也无法直接执行。



综述

如果单元测试选择 JUnit5(已是事实的主流),在 Android 如果只有单元测试可以使用 JUnit5,但如果还有界面测试的,选择 JUnit4,不要混用 JUnit5,其一 Robolectric 与 JUnit5 没什么兼容性,其支持的还是 JUnit4。其二是 Android 官网在讲述测试中,使用是 JUnit4。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虚妄皆空

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值