所有公共方法都使用私有变量的类如何编写可正常运行的单元测试?
问题根因
你当前的测试存在三个核心错误:
- 你直接将被测类
LoginInteractor声明为@Mock对象,Mock对象的所有方法默认返回对应类型的默认值:布尔类型返回false,对象类型返回null,这是两个测试用例失败的直接原因,你完全没有调用LoginInteractor的真实逻辑。 - 初始化顺序错误:即使你不用Mock的
LoginInteractor,如果先初始化LoginInteractor再替换LoginSessionManager的单例,LoginInteractor构造时已经拿到了旧的LoginSessionManager实例,依赖替换不会生效。 - 方法调用写法错误:Java类的非
@JvmField成员方法在Kotlin中需要显式调用方法,你写的loginSessionManager.isLoggedIn是属性访问写法,不会匹配到Java的isLoggedIn()方法,导致Mock规则不生效。
修复方案
调整测试逻辑,只Mock被测类的依赖,调整初始化顺序保证依赖替换生效,修正方法调用写法即可,修复后代码如下:
@RunWith(PowerMockRunner::class) @PrepareForTest( TextUtils::class, LoginInteractor::class, LoginSessionManager::class ) class LoginInteractorTest : TestCase() { // 只Mock被测类的依赖LoginSessionManager @Mock private lateinit var loginSessionManager: LoginSessionManager // 声明真实的被测类实例 private lateinit var loginInteractor: LoginInteractor @Throws(Exception::class) public override fun setUp() { MockitoAnnotations.openMocks(this) // 第一步:先替换LoginSessionManager的全局单例 Whitebox.setInternalState(LoginSessionManager::class.java, "loginSessionManager", loginSessionManager) // 第二步:清空LoginInteractor旧的单例,让它重新初始化,此时构造函数会拿到我们替换后的mock实例 Whitebox.setInternalState(LoginInteractor::class.java, "instance", null as LoginInteractor?) // 获取真实的被测类实例 loginInteractor = LoginInteractor.getInstance() // 也可以跳过上面两步,直接强制替换LoginInteractor内部的依赖,效果一致: // loginInteractor = Whitebox.invokeConstructor(LoginInteractor::class.java) // Whitebox.setInternalState(loginInteractor, "loginSessionManager", loginSessionManager) } @Test fun isLoggedInTest(){ // 修正为方法调用写法 `when`(loginSessionManager.isLoggedIn()).thenReturn(true) assertEquals(true, loginInteractor.isLoggedIn()) } @Test fun getLoginIntentTest(){ val expectIntent = Intent() `when`(loginSessionManager.getLoginIntent()).thenReturn(expectIntent) assertEquals(expectIntent, loginInteractor.getLoginIntent()) } }
内容的提问来源于stack exchange,提问作者Light Yagami
相关产品推荐
相关产品推荐

