Android单元测试中Mock AuthToken方法失败问题排查
问题:Mock AuthToken方法时触发错误,私有方法仍执行的原因及解决办法
代码与错误场景
AuthToken数据类代码
@JsonClass(generateAdapter = true) data class AuthToken( @Json(name = "token") val access: String, @Json(name = "refreshToken") val refresh: String ) { fun isAccessTokenValid(): Boolean = isTokenValid(access) fun isRefreshTokenValid(): Boolean = isTokenValid(refresh) private fun isTokenValid(token: String): Boolean { return try { val jwt = JWT(token) val isExpired = jwt.isExpired(0) Timber.d("Token ${if (isExpired) "expired" else "valid"} [${jwt.details()}]") !isExpired } catch (e: DecodeException) { Timber.w(e, "Invalid auth token.") false } } }
单元测试代码
val authToken = AuthToken("accessToken", "refreshToken") every { authToken.isAccessTokenValid() } returns true every { authToken.isRefreshTokenValid() } returns false
触发的错误
Missing mocked calls inside every { ... } block: make sure the object inside the block is a mock.
错误发生在val jwt = JWT(token)行。
原因分析
- 操作的是真实对象而非Mock对象:直接用
AuthToken()构造的是真实实例,Mock框架(比如MockK)无法拦截真实对象的方法调用,调用isAccessTokenValid()时会直接执行真实逻辑,包括内部调用的私有方法isTokenValid,自然会走到JWT(token)这行代码。 every { ... }只对Mock实例生效:这个语法是用来定义Mock对象的方法返回值,真实对象调用时框架识别不到Mock配置,所以报错提示缺少Mock调用。
解决方法
方法1:直接Mock AuthToken对象
用MockK的mockk<AuthToken>()创建Mock实例,替代真实构造:
val authToken = mockk<AuthToken>() every { authToken.isAccessTokenValid() } returns true every { authToken.isRefreshTokenValid() } returns false
这样框架会完全拦截方法调用,不会执行任何真实逻辑,包括内部的私有方法。
方法2:重构代码,依赖注入JWT校验逻辑
如果需要测试AuthToken本身的逻辑(而非单纯Mock结果),建议把JWT校验逻辑抽成独立依赖,方便Mock:
- 定义JWT校验接口与实现类:
interface JwtValidator { fun isTokenValid(token: String): Boolean } class DefaultJwtValidator : JwtValidator { override fun isTokenValid(token: String): Boolean { return try { val jwt = JWT(token) val isExpired = jwt.isExpired(0) Timber.d("Token ${if (isExpired) "expired" else "valid"} [${jwt.details()}]") !isExpired } catch (e: DecodeException) { Timber.w(e, "Invalid auth token.") false } } }
- 修改AuthToken类,注入JwtValidator:
@JsonClass(generateAdapter = true) data class AuthToken( @Json(name = "token") val access: String, @Json(name = "refreshToken") val refresh: String, private val jwtValidator: JwtValidator = DefaultJwtValidator() // 默认使用真实实现 ) { fun isAccessTokenValid(): Boolean = jwtValidator.isTokenValid(access) fun isRefreshTokenValid(): Boolean = jwtValidator.isTokenValid(refresh) }
- 测试时Mock JwtValidator:
val mockValidator = mockk<JwtValidator>() val authToken = AuthToken("accessToken", "refreshToken", mockValidator) every { mockValidator.isTokenValid("accessToken") } returns true every { mockValidator.isTokenValid("refreshToken") } returns false
这种方式既能验证AuthToken的方法逻辑是否正确调用了校验器,又能避免执行真实的JWT解析代码。
内容的提问来源于stack exchange,提问作者tzegian
相关产品推荐
相关产品推荐

