test FirebaseFirestore with JUnit using await, error - android

I am using:
val data = firestore.collection("add").document("departament").collection("anuncios").document(id).get()
.await()
return data.toObject(Anuncios::class.java)
and in my test I wrote like this:
#Test
fun `Get Add by id correct, should return data`() = runBlockingTest {
//GIVEN
coEvery {
firestore.collection("add").document("departament").collection("anuncios")
.document("01sm3zv3aCzGrWX3ycPT").get()
} returns dataTask
// WHEN
adRepository.getAnuncioId("01sm3zv3aCzGrWX3ycPT").apply {
// THEN
Truth.assertThat(this).isEqualTo(Anuncios())
coVerify(exactly = 1) {
firestore.collection("add").document("departament").collection("anuncios")
.document("01sm3zv3aCzGrWX3ycPT").get().await()
}
verify(exactly = 1) { datoSnap.toObject(Anuncios::class.java)!! }
}
}
the error throws me:
no answer found for: Task(#6).isComplete()
There is a problem with the await, but I don't know how to do it, I am new to unit test

Check out this and this, you can't run a test and wait for a real response, you have to mock it.

I solved it with:
mockkStatic("kotlinx.coroutines.tasks.TasksKt")
since this mocks the functions that are required

Related

how to throw an exception on a retrofit call

I am making an api call using retrofit and I want to write a unit test to check if it returns an exception.
I want to force the retrofit call to return an exception
DataRepository
class DataRepository #Inject constructor(
private val apiServiceInterface: ApiServiceInterface
) {
suspend fun getCreditReport(): CreditReportResponse {
try {
val creditReport = apiServiceInterface.getDataFromApi() // THIS SHOULD RETURN AN EXCEPTION AND I WANT TO CATCH THAT
return CreditReportResponse(creditReport, CreditReportResponse.Status.SUCCESS)
} catch (e: Exception) {
return CreditReportResponse(null, CreditReportResponse.Status.FAILURE)
}
}
}
ApiServiceInterface
interface ApiServiceInterface {
#GET("endpoint.json")
suspend fun getDataFromApi(): CreditReport
}
I have written a test case for getCreditReport which should validate the failure scenario
#Test
fun getCreditReportThrowException() {
runBlocking {
val response = dataRepository.getCreditReport()
verify(apiServiceInterface, times(1)).getDataFromApi()
Assert.assertEquals(CreditReportResponse.Status.FAILURE, response.status)
}
}
so to make the above test case pass, I need to force the network call to throw and exception
please suggest
Thanks
R
Actually #Vaibhav Goyal provided a good suggestion to make your testing as easier. Assuming you are using MVVM structure, in your test cases you can inject a "mock" service class to mock the behaviours that you defined in the test cases, so the graph will be like this
Since I am using mockk library at the moment, the actual implementation in your code base would be a little bit different.
#Test
fun test_exception() {
// given
val mockService = mockk<ApiServiceInterface>()
val repository = DataRepository(mockService)
every { mockService.getDataFromApi() } throws Exception("Error")
// when
val response = runBlocking {
repository.getCreditReport()
}
// then
verify(exactly = 1) { mockService.getDataFromApi }
assertEquals(CreditReportResponse.Status.FAILURE,response.status)
}
But if you want to test the exception thrown from Retrofit, then you might need mockServer library from square to help you to achieve this https://github.com/square/okhttp#mockwebserver
And the graph for this would be like this
You also have to setup the mock server to do so
#Test
fun test_exception_from_retrofit() {
// can put in the setup method / in junit4 rule or junit5 class
val mockWebServer = MockWebServer()
mockWebServer.start()
// given
val service = Retrofit.Builder()
.baseUrl(mockWebServer.url("/").toString())
.build()
.create(ApiServiceInterface::class)
val repository = DataRepository(service)
// when
mockWebServer.enqueue(MockResponse()
.setResponseCode(500)
.setBody("""{"name":"Tony}""") // you can read the json file content and then put it here
)
val response = runBlocking {
repository.getCreditReport()
}
// then
verify(exactly = 1) { mockService.getDataFromApi }
assertEquals(CreditReportResponse.Status.FAILURE,response.status)
// can put in tearDown / in junit4 rule or juni5 class
mockWebServer.shutdown()
}
SO you can test different exception like json format invalid, 500 status code,data parsing exception
Bonus point
Usually I would put the testing json under test directory and make it almost same as the api path for better maintainence

Unit test two api calls within a method

I have a method that makes an API call and if an error occurs it will retry the call with a different instance of the same service API.
var getResponse = myApi?.getCodeApi()
if (getResponse?.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
// Retrying with instance of service with a different token
getResponse = newMyApiService?.getCodeApi()
}
checkResponse(getResponse)
What is the right way to unit test the above code?. I tried something like this and it does not seem to work.
whenever(myAPi.getCodeApi()).thenReturn(properResponse)
val errorResponse : Response<DataModel> = mock()
whenever(response.code()).thenReturn(HttpsURLConnection.HTTP_UNAUTHORIZED)
whenever(myAPi.getCodeApi()).thenReturn(errorResponse)
test.callMethod()
assertValues(..,..,..)
I would test the above the code in below ways, i use mockito kotlin but i think this will help for what you are looking for i:e; right way ( that is subjective) :
#Test
fun `retry with newMyApiService when myAPI returns HTTP_UNAUTHORIZED`() {
myApi.stub {
on {
getCodeApi() } doReturn Erorr_Response_Model
}
newMyApiService.stub {
on {
getCodeApi() } doReturn Response_Model
}
test.callMethod();
verify(newMyApiService, times(1)). getCodeApi()
Assertions.assert(..Above Response_Model )
}
And a test to make sure that newAPIService does not always get called:
#Test
fun `myApi should return the valid result without retrying`() {
myApi.stub {
on {
getCodeApi() } doReturn SuccessModel
}
test.callMethod();
verify(newMyApiService, times(0)). getCodeApi()
verify(myApi, times(1)). getCodeApi()
Assertions.assert(..SuccessModel )
}

Use Spek with suspend method

I am trying to create a unit test using Spek framework and nhaarman mockito kotlin in my Android Kotlin project. The problem is that when there is nested suspend method I don't know how to mock response.This is how I'm trying
I defined:
val testCoroutineDispatcher = TestCoroutineDispatcher()
val testCoroutineScope = TestCoroutineScope(testCoroutineDispatcher)
and before any describe
beforeGroup {
Dispatchers.setMain(testCoroutineDispatcher) //not sure if this is working properly
}
afterGroup {
Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
testCoroutineScope.cleanupTestCoroutines()
}
and this is my group
describe("Test view model") {
val contentRepository by memoized(CachingMode.SCOPE) { mock<ContentRepository>() }
val contentViewModel by memoized(CachingMode.SCOPE) {
ContentViewModel(contentRepository)
}
describe("When something happens") {
beforeGroup {
testCoroutineScope.runBlockingTest {
whenever(contentRepository.fetchAllContents(0, 10))
.thenReturn(Result.success(content))//This is suspend
contentViewModel.setContentPage(0)
}
}
it("should fetch all content from repository with page 0") {
verifyBlocking(contentRepository) {
fetchAllClassContents(0, 10)
}
}
}
}
})
But Im getting the following error
Argument(s) are different! Wanted:
classContentRepository.fetchAllClassContents(
0,
10,
Continuation at viewmodel.ContentViewModelSpek$1$3$1$4$1.invokeSuspend(ContentViewModelSpek.kt:92)
);
-> at repository.ContentRepository.fetchAllClassContents(ContentRepository.kt:23)
Actual invocation has different arguments:
contentRepository.fetchAllContents(
0,
10,
Continuation at viewmodel.ContentViewModel$setContentPage$1.invokeSuspend(ContentViewModel.kt:26)
);
It seem like mock, method execution and assertion are running in different scopes
I can't find any guide that helps me create test with coroutine
Thanks in advance

Testing Android Room with Kotlin Flow

I'm trying to test a Room DAO exposing functions that return Flows. The following test won't pass and I'm struggling to see why :
#Test
fun `observeHomeCoursesFeatured() does not return courses that are no longer featured`() = runBlocking {
val outputList: MutableList<List<HomeCourse>> = mutableListOf()
launch { subject.observeHomeCoursesFeatured().collect { outputList.add(it) } }
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()))
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1()))
assertEquals(2, outputList.size)
assertEquals(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()), outputList[0])
assertEquals(listOf(getHomeCourseFeatured1()), outputList[1])
}
It fails at assertEquals(2, outputList.size) saying that outputList is empty.
This test passes :
#Test
fun `observeHomeCoursesFeatured() does not return courses that are no longer featured`() = runBlocking {
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()))
assertEquals(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()), subject.observeHomeCoursesFeatured().first())
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1()))
assertEquals(listOf(getHomeCourseFeatured1()), subject.observeHomeCoursesFeatured().first())
}
The second test passing, shows that my DAO is working fine and it is more a question of threading and concurrency between the test thread and the thread that Room uses to trigger Flow changes.
I already added #get:Rule val archRule = InstantTaskExecutorRule() in my test. I also build my test DB with this :
db = Room.inMemoryDatabaseBuilder(ctx, CoreDatabase::class.java)
.setTransactionExecutor(Executors.newSingleThreadExecutor())
.allowMainThreadQueries()
.build()
What am I missing ?
launch is asynchronous, so you have a race condition.
#Test
fun `observeHomeCoursesFeatured() does not return courses that are no longer featured`() = runBlocking {
val job = async { subject.observeHomeCoursesFeatured().take(2).toList() }
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()))
subject.saveHomeCoursesFeatured(listOf(getHomeCourseFeatured1()))
val outputList = job.await()
assertEquals(2, outputList.size)
assertEquals(listOf(getHomeCourseFeatured1(), getHomeCourseFeatured2()), outputList[0])
assertEquals(listOf(getHomeCourseFeatured1()), outputList[1])
}

Mockk - MockKException when testing password against regex

I've just started to do Unit testing in Kotlin using Mockk.
I'm trying to test the following function:
fun evaluatePredicate(regEx: String, passwordInserted: String) : Boolean {
return passwordInserted.matches(regEx.toRegex())
}
My test look like this:
#Test
fun evaluatePredicate_shouldContainLowerCase_trueExpected() {
//given
val regEx = ".*[a-z]+.*" //lower case
val password = "password"
every { password.matches(regEx.toRegex()) } returns true
every { SUT.evaluatePredicate(regEx, password) } returns true
//when
val evaluate = password.matches(regEx.toRegex())
val result = SUT.evaluatePredicate(regEx, password)
//then
assertEquals(evaluate, result)
}
But I'm getting :
io.mockk.MockKException: Missing calls inside every { ... } block.
at line:
every { password.matches(regEx.toRegex()) } returns true
I've tried to use Mockk Matcher any() instead of matches(regEx.toRegex()) but nothing changed.
I'm not sure if I'm using the right tools for the job here.
Any suggestion is welcome.

Categories

Resources