Hot questions for Using Mockito in firebase

Question:

I am attempting to implement testing for an android app that I'm building. One of the tests involves an interactor file I wrote (DatabaseInteractor.java) that connects to Google Firestore. The default constructor sets the FirestoreFirebase object to FirebaseFirestore.getInstance();.

I am using Mockito & JUnit for my unit tests. When I attempt to create a DataBaseInteractor object in my Unit Test, I get the following error:

java.lang.IllegalStateException: Default FirebaseApp is not initialized in this process null. Make sure to call FirebaseApp.initializeApp(Context) first.

Apparently this error occurs when the default constructor is called in my Unit Test. I'm not sure why this is happening. I am able to create a DataBaseInteractor object in other parts of my android app without issue. Are there perhaps limitations to when and where I can initialize a class that utilizes FireBaseFireStore objects? If I want to mock the behavior of FireBase for unit testing, how do I get around these restrictions?

EDIT: A truncated version of my DataBaseInteractor.java file:

public class DatabaseInteractor {
    private static User theUser;
    private FirebaseFirestore db;
    private DocumentReference userData;


    public DatabaseInteractor() {
        db = FirebaseFirestore.getInstance();
        theUser = new User();
    }
 // ... various methods that add/retrieve Users from FireStore ... //
}

Answer:

You need to inject the FirebaseFirestore instance in your constructor so that it can be mocked, something like:

public class DatabaseInteractor {
    private static User theUser;
    private FirebaseFirestore db;
    private DocumentReference userData;


    public DatabaseInteractor(FirebaseFirestore firestore) {
        db = firestore
        theUser = new User();
    }
 // ... various methods that add/retrieve Users from FireStore ... //
}  

The in your test you can mock FirebaseFirestore with Mockito:

@Test
public void someTest() {
  FirebaseFirestore mockFirestore = Mockito.mock(FirebaseFirestore.class)
  Mockito.when(mockFirestore.someMethodCallYouWantToMock()).thenReturn(something)

  DatabaseInteractor interactor = new DatabaseInteractor(mockFirestore)

  // some assertion or verification
}

I'd suggest you read about dependency injection, it will make unit testing a lot easier if you can pass in mocks of your dependencies in the constructor instead of instantiating them inside it.

Question:

I have a utility class in my Android code that handles the authentication of users. I'm writing unit tests for this class with Mokcito to verify that a listener is being notified if the creation of a new user had succeeded or failed. Here is one of this utility class's methods:

public void createNewUser(String email, String password) {
    firebaseAuth.createUserWithEmailAndPassword(email, password)
            .addOnSuccessListener(authResult -> {
                authListener.newUserCreated();
            })
            .addOnFailureListener(e -> {
                authListener.failedCreatingNewUser();
            });
}

I am mocking FirebaseAuth and I want to verify that authListener.newUserCreated() has been called. I have tried using deep stubs and argument captors to handle the chained method calls on firebaseAuth.createUserWithEmailAndPassword but I can't figure out how to get this working.

UPDATE

Here is my test class with the test for this method:

public class AuthUtilsTest {

    private static final String USERNAME = "USERNAME";
    private static final String PASSWORD = "PASSWORD";

    @Mock
    private FirebaseAuth firebaseAuth;

    @Mock
    private FirebaseFirestore firebaseFirestore;

    @Mock
    private BaseEncoding base64;

    @Mock
    private PreferencesRepo preferencesRepo;

    @Mock
    private AuthUtilsContract.EventListener eventListener;

    private AuthUtils authUtils;

    @Before
    public void setupAuthUtils() {
        MockitoAnnotations.initMocks(this);

        authUtils = new AuthUtils(
                preferencesRepo,
                firebaseAuth,
                firebaseFirestore,
                base64
        );

        authUtils.takeEventListener(eventListener);
    }

    @Test
    public void failureCreatingNewUserTellsListener() {
        Task<AuthResult> failedTask = Tasks.forException(new Exception("fail"));
        when(firebaseAuth.createUserWithEmailAndPassword(anyString(), anyString())).thenReturn(failedTask);

        authUtils.createNewUser(USERNAME, PASSWORD);

        verify(eventListener).failedCreatingNewUser();
    }

}

which throws the exception

java.lang.ExceptionInInitializerError at com.google.android.gms.tasks.zzn.addOnSuccessListener(Unknown Source) ... Caused by: java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked.


Answer:

Use Mockito.when to make the createUserCall return a mocked Task. Then Mockito.verify on the task to capture the arguments to the add listener call.

Test the captured arguments to the extent you heart desires (this is like a unit test within a unit test, the captured arguments are your new Classes under test).

This method won't actually test that the listeners are called. Just that the add listener method was called and that the callbacks do what they should when called

verify(mockTask).addOnSuccessListener(listenerCaptor.capture());
OnSuccessListener<Auth> newObjectUnderTest = listenerCaptor.getValue();

//ACT
newObjectUnderTest.onSuccess(auth);

//ASSERT
verify(authListener).newUserCreated();

Question:

I'm fairly new to android & java. I'm trying to write a unit test that verifies that Firebase method is called with correct parameters & that specific call back is fired on success

public class RegistrationViewModel {

private iRegistrationViewCallback viewCallback;
private FirebaseAuth mAuth;
public  String email;
public String password;

public RegistrationViewModel(iRegistrationViewCallback view, FirebaseAuth mockAuth) {
    viewCallback = view;
    mAuth = mockAuth;
}

public void createUser() {

    mAuth.createUserWithEmailAndPassword(email, password)
            .addOnCompleteListener((Executor) this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    viewCallback.doSomething();
                }
            });

}

public void setEmail(String email) {
    this.email = email;
}

public void setPassword(String password) {
    this.password = password;
}
}

My Unit test looks like this:

public class RegistrationViewModelUnitTest {

private RegistrationViewModel viewModelUnderTest;
private iRegistrationViewCallback mockView;
private FirebaseAuth mockAuth;


@Before
public void SetUp() {
    mockView = mock(iRegistrationViewCallback.class);
    mockAuth = mock(FirebaseAuth.getInstance().getClass());
    viewModelUnderTest = new RegistrationViewModel(mockView,mockAuth);
}

@Test
public void progressBarIsDismissedWhenRegistrationIsSuccessFul() throws Exception {
    String email = "somusername";
    String password = "somePassword";
    viewModelUnderTest.email = email;
    viewModelUnderTest.password = password;
    viewModelUnderTest.createUser();
    verify(mockAuth).createUserWithEmailAndPassword(email,password);
    verify(mockView).doSomething();
}

}

verify(mockView).doSomething(); obviously fails.

What would be the best way to verify that viewCallback.doSomething(); is called on success. Should I somehow stub the onComplete? or is there a different way of doing this? I have thought of putting Firebase code behind an interface but still cant figure out how to verify that the call back is fired. I'm using Mockito for mocking.


Answer:

This seems more like an intergration test scenario.

If you want to unit test it i would do the following:

Step 1)

Create a public class with the callback behavior, instead of using an anonymous class:

public class MyOnCompleteListener implements OnCompleteListener<AuthResult>() {
      @Override
      public void onComplete(@NonNull Task<AuthResult> task) {
          viewCallback.doSomething();
      }
});

Step 2)

Change your createUser method to:

public void createUser() {

    mAuth.createUserWithEmailAndPassword(email, password)
            .addOnCompleteListener((Executor) this
                       , new MyOnCompleteListener<AuthResult>() );

}

Step 3)

Define your test as follows:

@Test
public void progressBarIsDismissedWhenRegistrationIsSuccessFul() throws Exception {
    String email = "somusername";
    String password = "somePassword";
    viewModelUnderTest.email = email;
    viewModelUnderTest.password = password;

    Task taskMock = mock(Task.class);
    when(mockAuth.createUserWithEmailAndPassword (email, password)).thenReturn(taskMock);

    viewModelUnderTest.createUser();
    verify(mockAuth, times(1)).createUserWithEmailAndPassword(email,password);
    verify(taskMock, times(1)).addOnCompleteListener(any(RegistrationViewModel.class)
          , any(MyOnCompleteListener.class));
}

The bottom line is that your verify that a certain callback is registered for the createUserWithEmailAndPassword method.

Thats all you can do if you want to have this as a unit test without any actual invocation of framework code.