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

如何基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 14:03:28