如何基于Firebase、JUnit、Mockito实现Android登录单元测试
问题原因
测试跳过监听器直接结束,核心是3个写法错误:
- 声明了
@Mock修饰的mAuth,但测试方法中调用FirebaseAuth.getInstance()获取真实SDK实例,mock对象完全未生效 successTask、failureTask未做初始化,默认值为null,mock的方法返回值无效- Firebase原生
Task默认走异步线程执行,测试主线程不会等待异步回调执行完成,会直接跑完测试方法退出,监听器自然不会被触发
前置依赖
首先确保模块级build.gradle的测试依赖配置正确:
testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:4.11.0' testImplementation 'com.google.firebase:firebase-auth:22.1.2'
正确实现代码
核心思路是用Firebase SDK自带的Tasks工具类生成同步触发的测试Task,避免异步等待问题;同时通过依赖注入把mock的FirebaseAuth实例传入业务类,不要在业务代码中硬编码FirebaseAuth.getInstance()。
@RunWith(MockitoJUnitRunner.class) public class LoginActivityTest { // JUnit4不需要继承TestCase类 @Mock FirebaseAuth mAuth; @Mock AuthResult mockAuthResult; @Mock FirebaseUser mockUser; private Task<AuthResult> successTask; private Task<AuthResult> failureTask; // 测试用固定常量 private static final String VALID_EMAIL = "goku@gmail.com"; private static final String VALID_PWD = "testtest"; private static final String INVALID_EMAIL = "invalid@gmail.com"; private static final String WRONG_PWD = "wrongpass123"; @Before public void setUp() { // 生成同步触发的成功Task,调用后立刻执行成功回调 successTask = Tasks.forResult(mockAuthResult); // 生成同步触发的失败Task,调用后立刻抛出指定认证异常 failureTask = Tasks.forException(new FirebaseAuthInvalidCredentialsException("ERROR_INVALID_CREDENTIAL", "邮箱或密码错误")); // 配置mock规则:合法账号密码返回成功Task,错误凭证返回失败Task when(mAuth.signInWithEmailAndPassword(VALID_EMAIL, VALID_PWD)).thenReturn(successTask); when(mAuth.signInWithEmailAndPassword(INVALID_EMAIL, WRONG_PWD)).thenReturn(failureTask); // 配置mockAuthResult返回模拟用户 when(mockAuthResult.getUser()).thenReturn(mockUser); } @Test public void testLoginWithValidCredential_shouldSuccess() { // 注意:业务类(LoginViewModel/LoginActivity)必须支持传入FirebaseAuth实例 // 禁止在业务类内部直接写死FirebaseAuth.getInstance(),否则mock无法替换真实实例 LoginViewModel loginViewModel = new LoginViewModel(mAuth); // 执行登录逻辑 loginViewModel.login(VALID_EMAIL, VALID_PWD); // 验证SDK方法被正确参数调用 verify(mAuth).signInWithEmailAndPassword(VALID_EMAIL, VALID_PWD); // 验证自身业务逻辑:比如登录成功状态标记正确、跳转逻辑触发 assertEquals(true, loginViewModel.getLoginSuccessLiveData().getValue()); } @Test public void testLoginWithInvalidCredential_shouldThrowError() { LoginViewModel loginViewModel = new LoginViewModel(mAuth); loginViewModel.login(INVALID_EMAIL, WRONG_PWD); verify(mAuth).signInWithEmailAndPassword(INVALID_EMAIL, WRONG_PWD); // 验证错误提示逻辑被触发 assertEquals("邮箱或密码错误", loginViewModel.getLoginErrorLiveData().getValue()); } }
注意事项
- 单元测试不需要验证Firebase SDK本身的逻辑,SDK逻辑由Google官方测试覆盖,你只需要验证自身业务逻辑在SDK返回成功/失败时的行为是否符合预期
- 如果直接测试Activity,可通过FragmentScenario/ActivityScenario结合依赖注入框架替换
FirebaseAuth实例为mock对象,不要直接在测试中启动真实SDK逻辑 - 不要手动mock Task的
addOnSuccessListener/addOnCompleteListener方法,Tasks.forResult()已经实现了同步回调逻辑,手动mock容易出现回调不触发的问题
内容的提问来源于stack exchange,提问作者Genielson
相关产品推荐
相关产品推荐

