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())
}