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

Android Studio中Firebase登录方法单元测试正确性咨询

问题:Firebase登录单元测试是否正确?

我正在做大学项目,作为单元测试新手,想确认我在Android Studio中针对Firebase登录方法写的单元测试是否正确。以下是我的注册方法代码和登录测试代码:

注册方法代码

public void signUp(String nickname, String email, String password,String dataOfBirth, String gender){
        auth = FirebaseAuth.getInstance();
        referenceDb = FirebaseDatabase.getInstance().getReference();
        auth.createUserWithEmailAndPassword(email,password)
                .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {

                            String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();

                            UserDao userDao = new UserDao();
                            User currUser = new User(uid, nickname, email, password, dataOfBirth, gender);

                            referenceDb.child("Users").child(uid).setValue(currUser).addOnCompleteListener(new OnCompleteListener<Void>() {
                                @Override
                                public void onComplete(@NonNull Task<Void> task) {
                                    if (task.isSuccessful()) {

                                       Toast.makeText(singUp_fragment.getActivity(), "SingUp successful", Toast.LENGTH_SHORT).show();
                                        openHomeFragment();
                                    } else {
                                        Toast.makeText(singUp_fragment.getActivity(), "SingUp failed. Please try again", Toast.LENGTH_SHORT).show();
                                        singUp_fragment.progressBar.setVisibility(View.GONE);
                                    }
                                }
                            });
                        } else {
                            Resources resources = singUp_fragment.getStartActivity().getResources();
                            singUp_fragment.cleanTextInputErrors();
                            try {
                                throw task.getException();
                            } catch (NoInternetConnectionException e) {
                                MotionToast.display(
                                        singUp_fragment.getStartActivity(),
                                        R.string.toast_warning_internet_connection,
                                        MotionToastType.WARNING_MOTION_TOAST
                                );
                            } catch (FirebaseAuthInvalidUserException e) {
                                singUp_fragment.setTextInputNicknameError(
                                        resources.getString(R.string.fragment_sign_up_invalid_nickname)
                                );
                            } catch (FirebaseAuthUserCollisionException e) {
                                singUp_fragment.setTextInputEmailError(
                                        resources.getString(R.string.fragment_sign_up_invalid_login)
                                );
                            } catch (Exception e) {
                                MotionToast.display(
                                        singUp_fragment.getStartActivity(),
                                        R.string.toast_error_unknown_error,
                                        MotionToastType.ERROR_MOTION_TOAST
                                );
                            }
                        }
                        singUp_fragment.progressBar.setVisibility(View.GONE);
                        singUp_fragment.setEnabledButtonSignUp(true);
                    }
                });
    }

登录测试代码

public class SingIn_Test {
    FirebaseAuth auth;
    boolean test;

    @Test
    public void ValidEmailPasswordTest(){

        test = false;
        auth = FirebaseAuth.getInstance();

        auth.signInWithEmailAndPassword("annal@live.it", "Annamaria2222").addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Assert.assertTrue(true);

                } else {
                    Assert.assertFalse(false);
                }
                test = true;
            }
        });
        while(!test){

        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void InvalidEmptyEmailPasswordTest(){

        test = false;
        auth = FirebaseAuth.getInstance();

        auth.signInWithEmailAndPassword("", "").addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Assert.assertTrue(true);

                } else {
                    Assert.assertFalse(false);
                }
                test = true;
            }
        });
        while(!test){

        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void InvalidEmptyEmailTest(){

        test = false;
        auth = FirebaseAuth.getInstance();

        auth.signInWithEmailAndPassword("", "Annamaria2222").addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Assert.assertTrue(true);

                } else {
                    Assert.assertFalse(false);
                }
                test = true;
            }
        });
        while(!test){

        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void InvalidEmptyPasswordTest(){

        test = false;
        auth = FirebaseAuth.getInstance();

        auth.signInWithEmailAndPassword("annal@live.it", "").addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Assert.assertTrue(true);

                } else {
                    Assert.assertFalse(false);
                }
                test = true;
            }
        });
        while(!test){

        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void InvalidFormatEmailPasswordTest(){

        test = false;
        auth = FirebaseAuth.getInstance();

        auth.signInWithEmailAndPassword("annal@live", "Annamaria").addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Assert.assertTrue(true);

                } else {
                    Assert.assertFalse(false);
                }
                test = true;
            }
        });
        while(!test){

        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void InvalidFormatPasswordTest(){

        test = false;
        auth = FirebaseAuth.getInstance();

        auth.signInWithEmailAndPassword("annal@live.it", "Annamaria").addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Assert.assertTrue(true);

                } else {
                    Assert.assertFalse(false);
                }
                test = true;
            }
        });
        while(!test){

        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void InvalidFormatEmailTest(){

        test = false;
        auth = FirebaseAuth.getInstance();

        auth.signInWithEmailAndPassword("annal@live", "Annamaria2222").addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Assert.assertTrue(true);

                } else {
                    Assert.assertFalse(false);
                }
                test = true;
            }
        });
        while(!test){

        }
    }

}

请问这些测试代码是否正确?


回答

你的测试代码存在不少问题,下面逐一说明并给出改进方向:

1. 忙等待的性能问题

你用while(!test){}等待异步回调完成,这是典型的忙等待,会持续占用CPU资源,还可能因回调未触发、超时导致测试卡死。应该改用JUnit的CountDownLatch处理异步逻辑:

@Test
public void ValidEmailPasswordTest() throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(1);
    auth = FirebaseAuth.getInstance();

    auth.signInWithEmailAndPassword("annal@live.it", "Annamaria2222").addOnCompleteListener(task -> {
        if (task.isSuccessful()) {
            Assert.assertTrue(task.isSuccessful());
        } else {
            Assert.fail("登录失败: " + task.getException().getMessage());
        }
        latch.countDown();
    });
    latch.await(5, TimeUnit.SECONDS); // 设置超时时间,避免无限等待
}

2. 断言逻辑无效

Assert.assertTrue(true)和Assert.assertFalse(false)永远不会失败,完全起不到验证结果的作用。正确做法是基于任务结果断言:

  • 成功场景:Assert.assertTrue(task.isSuccessful())
  • 失败场景:Assert.assertFalse(task.isSuccessful()),还可以进一步验证异常类型

3. 异常预期错误

你在多个测试上标注@Test(expected = IllegalArgumentException.class),但Firebase的signInWithEmailAndPassword方法在传入空值、格式错误时,不会直接抛出IllegalArgumentException,而是将异常封装在task.getException()中(比如FirebaseAuthInvalidCredentialsException)。这个注解永远不会触发,会导致测试错误通过。

正确的异常验证方式是在回调中检查:

@Test
public void InvalidEmptyEmailTest() throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(1);
    auth = FirebaseAuth.getInstance();

    auth.signInWithEmailAndPassword("", "Annamaria2222").addOnCompleteListener(task -> {
        Assert.assertFalse(task.isSuccessful());
        Assert.assertNotNull(task.getException());
        Assert.assertTrue(task.getException() instanceof FirebaseAuthInvalidCredentialsException);
        latch.countDown();
    });
    latch.await(5, TimeUnit.SECONDS);
}

4. 依赖真实服务,不属于单元测试

当前测试直接调用真实Firebase Auth接口,属于集成测试,而非单元测试。单元测试需要隔离外部依赖,可使用Firebase测试SDK或Mockito等Mock框架模拟FirebaseAuth行为,避免依赖网络、真实账号状态,让测试更快更稳定。

示例Mock代码:

public class SingIn_Test {
    @Mock
    FirebaseAuth auth;
    @Mock
    Task<AuthResult> mockTask;

    @Before
    public void setup() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void validEmailAndPassword_shouldSignInSuccessfully() throws InterruptedException {
        when(auth.signInWithEmailAndPassword("annal@live.it", "Annamaria2222")).thenReturn(mockTask);
        when(mockTask.isSuccessful()).thenReturn(true);

        CountDownLatch latch = new CountDownLatch(1);
        auth.signInWithEmailAndPassword("annal@live.it", "Annamaria2222").addOnCompleteListener(task -> {
            Assert.assertTrue(task.isSuccessful());
            latch.countDown();
        });
        latch.await(5, TimeUnit.SECONDS);
    }
}

5. 测试命名不清晰

测试方法名应遵循场景+预期的规则,比如validEmailAndPassword_shouldSignInSuccessfully,这样能直观看出测试目的。

总结

你的测试思路没问题,但实现细节存在较多错误,尤其是异步处理、断言逻辑和异常验证部分。建议先改用CountDownLatch处理异步,修正断言逻辑,再学习Mock框架隔离外部依赖,将测试从集成测试转为真正的单元测试。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 00:30:41