getting error mocking firebase user with mockito

I'm trying to mock a firebase user to provide it for my test , i have mocked the firebase auth class from which i get the uid of the currently signed in user but for some it keeps throwing the same error , i would appreciate your helps guys

  • This is my production code
    private fun getUserProfileInfo(){
        val db = ServiceLocator.reference
        val child = db.child("Users")
        val user = ServiceLocator.firebaseUser
        val value = child.child(user!!.uid)

           value.addListenerForSingleValueEvent(object  : ValueEventListener {
                override fun onDataChange(snapshot: DataSnapshot) {
                    if(snapshot.exists()){
                        val model = snapshot.getValue(ProfileModel::class.java)
                        userName = model!!.firstName
                        userPic = model.profilePicUrl
                    }
                }
                override fun onCancelled(error: DatabaseError) {
                }

            })
    }
  • This is my testing code
  @Test
    fun testCaseProfileInfo(){
        val firebaseUserMock = mock(FirebaseUser::class.java)
        val firebaseMock = mock(FirebaseAuth::class.java)
        val databaseMock = mock(DatabaseReference::class.java)

        ServiceLocator.firebaseAuth = firebaseMock
        ServiceLocator.firebaseUser = firebaseUserMock
        ServiceLocator.reference = databaseMock

        doReturn(firebaseUserMock).`when`(firebaseMock).currentUser
        val uid = doReturn("123").`when`(firebaseMock).uid // error here

        doReturn(databaseMock.child("Users")
            .child(uid!!))
            .`when`(databaseMock).get()
    }
  • This is my Service Locator Class
object ServiceLocator {
    var firebaseAuth = FirebaseAuth.getInstance()
    var reference=FirebaseDatabase.getInstance(Utils.DATABASE_URL).reference
}

  • Error generated ( after update )
org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
-> at com.revert.journey.app.chatui.FriendsRequestActivityTest.testCaseProfileInfo

  • Quick Update

 @Test
    fun testCaseProfileInfo(){
        val userMock = mock(FirebaseUser::class.java)
        val firebaseMock = mock(FirebaseAuth::class.java)
        val databaseMock = mock(DatabaseReference::class.java)

        ServiceLocator.firebaseAuth = firebaseMock
        ServiceLocator.firebaseUser = userMock
        ServiceLocator.reference = databaseMock

        doReturn(userMock).`when`(firebaseMock).currentUser
        doReturn("123").`when`(userMock)!!.uid

        doReturn(databaseMock
            .child("Users")
            .child("123")) //  ( null pointer exception on this string)..
            .`when`(databaseMock).get()
    }


Solution 1:

You also need to mock the returned user from FirebaseAuth as well, something like this

val userMock = mock(FirebaseUser::class.java)

doReturn(userMock).`when`(firebaseMock).currentUser
doReturn("123").`when`(userMock).uid

If you split out your production code into a few more steps (like this), you would probably see that currentUser is null

val db = ServiceLocator.reference
val child = db.child("Users")
val user = ServiceLocator.firebaseAuth.currentUser
val uid = user?.uid ?: return
val value = child.child(uid)

The important thing to remember with mocked classes is that all their methods will return null unless you specifically set a return for them.

For a complete mock of the calls above, you would need the following mocks

If you want your method to be callable, you would need the following mocks. Your test method is not interacting with Firebase at all - it is a fake class (a mock) so you have to specifically tell it what to return when you call methods or get attributes from it. Your goal for testing is to define those return values in such a way as to get some useful operation from your method you can test.

@Test
 fun testCaseProfileInfo(){
    val firebaseMock = mock(FirebaseAuth::class.java)
    val databaseMock = mock(DatabaseReference::class.java)
    val userMock = mock(FirebaseUser::class.java)
    val childMock = mock(Reference::class.java)
    
    ServiceLocator.firebaseAuth = firebaseMock
    ServiceLocator.reference = databaseMock
    
    
    // Tell the firebaseMock what to return when 
    // you call "currentUser"
    doReturn(userMock).`when`(firebaseMock).currentUser
    
    // Tell "userMock" to return "123" when you call
    // currentUser.uid in your production method
    doReturn("123").`when`(userMock).uid
    
    // Tell the database mock what to return when
    // you call db.child("Users")
    doReturn(childMock).`when`(databaseMock).child("Users")
    
    // Tell the users mock what to return when you
    // call db.child(uid)
    doReturn(childMock).`when`.usersMock("123")
    
    // Method should now be callable, but adding a listener for the event would be good to test
}

For a more complete example, consider the following method to get the user id if they are logged in, that you would like to test with mocks.

fun getUID(): String? {
    val uid = ServiceLocator.firebaseAuth.currentUser?.uid
    return uid
}

You could write tests for this as follows

@Test
fun testUserNotLoggedIn(){
    val firebaseMock = mock(FirebaseAuth::class.java)
    
    ServiceLocator.firebaseAuth = firebaseMock
    doReturn(null).`when`(firebaseMock).currentUser
    
    assertNull(getUID())
}
@Test
fun testUserLoggedIn(){
    val firebaseMock = mock(FirebaseAuth::class.java)
    val userMock = mock(FirebaseUser::class.java)
    
    ServiceLocator.firebaseAuth = firebaseMock
    doReturn(userMock).`when`(firebaseMock).currentUser
    doReturn("123").`when`(userMock).uid
    
    assertEqual("123", getUID())
}