You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Mockito或MockK模拟android.util.Patterns?

解决Android邮箱验证方法测试中的NullPointerException问题

我明白你遇到的困境了——直接测试这个validate方法时,因为android.util.Patterns.EMAIL_ADDRESS在单元测试环境下未被初始化,导致抛出NullPointerException,而且你没法直接替换这个系统依赖来测试。下面给你几个可行的解决方案,按推荐程度排序:

方案一:通过依赖注入重构代码(最推荐)

这种方式既解决了测试问题,还能让代码更解耦、更易维护。核心思路是把Pattern作为依赖传入,而不是直接硬编码依赖系统的Patterns.EMAIL_ADDRESS。

重构后的代码

// 把验证逻辑封装到类中,方便注入依赖
class EmailValidator(
    // 默认使用系统的邮箱正则,不影响原有业务逻辑
    private val emailPattern: Pattern = android.util.Patterns.EMAIL_ADDRESS
) {
    fun validate(email: String): Result {
        return if (emailPattern.matcher(email).matches()) {
            Result(true)
        } else {
            Result(false, "error")
        }
    }
}

// 假设你的Result类定义如下
data class Result(val isSuccess: Boolean, val errorMessage: String? = null)

测试代码

测试时,你可以传入自己手动创建的Pattern,完全脱离对Android系统类的依赖:

import org.junit.Test
import org.junit.Assert.*
import java.util.regex.Pattern

class EmailValidatorTest {
    private val testEmailPattern = Pattern.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}")
    private val validator = EmailValidator(testEmailPattern)

    @Test
    fun `valid email should return success result`() {
        val result = validator.validate("user@example.com")
        assertTrue(result.isSuccess)
        assertNull(result.errorMessage)
    }

    @Test
    fun `invalid email should return error result`() {
        val result = validator.validate("invalid-email-format")
        assertFalse(result.isSuccess)
        assertEquals("error", result.errorMessage)
    }
}

方案二:用反射替换Patterns.EMAIL_ADDRESS(无需重构代码)

如果不想改动原有业务代码,可以用反射在测试前初始化或替换Patterns.EMAIL_ADDRESS的静态字段,避免空指针。

测试类中的初始化代码

import org.junit.Before
import org.junit.Test
import org.junit.Assert.*
import java.util.regex.Pattern
import android.util.Patterns

class EmailValidationTest {
    @Before
    fun setupPattern() {
        // 通过反射获取Patterns类的EMAIL_ADDRESS静态字段
        val emailField = Patterns::class.java.getDeclaredField("EMAIL_ADDRESS")
        emailField.isAccessible = true
        // 设置为你自己的测试用Pattern
        val testPattern = Pattern.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}")
        emailField.set(null, testPattern)
    }

    @Test
    fun `valid email passes validation`() {
        val result = validate("test@domain.com")
        assertTrue(result.isSuccess)
    }

    @Test
    fun `invalid email fails validation`() {
        val result = validate("bad-email")
        assertFalse(result.isSuccess)
        assertEquals("error", result.errorMessage)
    }

    // 保留你原有的validate方法用于测试
    private fun validate(email: String): Result {
        return if (Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
            Result(true)
        } else {
            Result(false, "error")
        }
    }
}

方案三:使用Robolectric模拟Android环境

Robolectric是一个Android单元测试框架,它能模拟Android系统类的行为,让Patterns.EMAIL_ADDRESS正常初始化,这样你可以直接测试原有的validate方法,无需任何代码改动。

测试类示例

import org.junit.Test
import org.junit.Assert.*
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import android.util.Patterns

@RunWith(RobolectricTestRunner::class)
class EmailValidationTest {

    @Test
    fun `valid email returns success`() {
        val result = validate("user@test.com")
        assertTrue(result.isSuccess)
    }

    @Test
    fun `invalid email returns error`() {
        val result = validate("not-an-email")
        assertFalse(result.isSuccess)
        assertEquals("error", result.errorMessage)
    }

    // 原有的validate方法
    private fun validate(email: String): Result {
        return if (Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
            Result(true)
        } else {
            Result(false, "error")
        }
    }
}

注意事项

使用Robolectric需要在项目build.gradle中添加对应的依赖,它能帮你在JVM环境下模拟Android系统类的行为,让测试顺利执行。


内容的提问来源于stack exchange,提问作者BakaOne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:44:06