test with firebase database throwing error
I'm using firebase in my project and i'm testing all the functionalities which includes firebase components and i recently tried to test firebase database but it is throwing an error which i could not understand if someone could help , i would appreciate it , Thank you
- error that i'm getting
Attempt to invoke virtual method 'com.google.firebase.database.DatabaseReference com.google.firebase.database.DatabaseReference.child(java.lang.String)' on a null object reference
at com.revert.journey.app.chatui.ChatHomeActivityTest.testCaseSendMessage
- This is my testing code
@Test
fun testCaseSendMessage(){
val databaseMock = mock(DatabaseReference::class.java)
ServiceLocator.reference = databaseMock
`when`(databaseMock.child("Messages").child("1251515").setValue(Utils.message()))
.thenReturn(isNotNull())
}
- This is my real code
val messageMap = hashMapOf<String,Any>()
messageMap["userName"] = userName
messageMap["userMessage"] = message
messageMap["userPic"] = userPic
messageMap["messageTiming"] = Calendar.getInstance().timeInMillis.toString()
messageMap["chatImage"] = downloadUrl
messageMap["uid"] = firebaseAuth.currentUser!!.uid
ServiceLocator.reference
.child("Messages")
.child(System.currentTimeMillis().toString())
.setValue(messageMap).await()
- ServiceLocator class
object ServiceLocator {
var firebaseAuth = FirebaseAuth.getInstance()
var reference = FirebaseDatabase.getInstance().reference
}
- Image Sample from my firebase
- UPDATE
var base = ServiceLocator.reference.child("Messages")
var child = base.child(System.currentTimeMillis().toString())
child.setValue(messageMap).await()
Solution 1:
If you want to Mock the full call chain you could create mocks for the intermediate states like this (I don't know all the right types to use here for realtime database, but a similar approach works for Firestore)
val databaseMock = mock(DatabaseReference::class.java)
val childMock = mock(Reference::class.java)
val mockTask = mock(??) // set type to whatever "setValue" returns
doReturn(childMock).`when`(databaseMock).child(anyString())
doReturn(childMock).`when`(childMock).child(anyString())
doReturn(mockTask).`when`(childMock).setValue(any())
If you want to actually test that the correct value was set, you can add a listener to the mock to intercept the actual value passed to it
doAnswer { invocation ->
val args = invocation.arguments
val l = args[0] as Map<String,Any>
//add tests here to assert that the map values you sent are correct
null
}.`when`(childMock).setValue(any())
Debugging Tips
If you want to diagnose what is going on in a scenario like this you can change the chained call in your real code to something like the code below. Then if one of the calls returns null you will know exactly which one it is and can add the missing mock for it.
val db = ServiceLocator.reference
val cm = db.child("Messages")
val ct = cm.child(System.currentTimeMillis().toString())
val response = ct.setValue(messageMap)
response.await()
None of these call should access your database in the test, so the actual database schema does not matter. All that matters is getting the mocks set correctly (since you are using a mock database anyway)