-
Notifications
You must be signed in to change notification settings - Fork 426
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AC-1092: Add Tests for Observation Repository #1010
Merged
rishabh-997
merged 3 commits into
master
from
AC-1092-Add-tests-for-Observation-Repository
Aug 27, 2023
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
191 changes: 191 additions & 0 deletions
191
...src/test/java/com/openmrs/android_sdk/library/api/repository/ObservationRepositoryTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
package com.openmrs.android_sdk.library.api.repository | ||
|
||
import android.content.Context | ||
import com.google.gson.GsonBuilder | ||
import com.openmrs.android_sdk.library.OpenmrsAndroid | ||
import com.openmrs.android_sdk.library.api.RestApi | ||
import com.openmrs.android_sdk.library.dao.ObservationRoomDAO | ||
import com.openmrs.android_sdk.library.databases.AppDatabase | ||
import com.openmrs.android_sdk.library.databases.AppDatabaseHelper | ||
import com.openmrs.android_sdk.library.databases.entities.ObservationEntity | ||
import com.openmrs.android_sdk.library.models.Observation | ||
import com.openmrs.android_sdk.utilities.NetworkUtils | ||
import dagger.hilt.android.testing.HiltAndroidRule | ||
import dagger.hilt.android.testing.HiltAndroidTest | ||
import dagger.hilt.android.testing.HiltTestApplication | ||
import io.mockk.clearAllMocks | ||
import io.mockk.every | ||
import io.mockk.mockk | ||
import io.mockk.mockkStatic | ||
import okhttp3.mockwebserver.MockResponse | ||
import okhttp3.mockwebserver.MockWebServer | ||
import okio.buffer | ||
import okio.source | ||
import org.junit.After | ||
import org.junit.Assert.assertEquals | ||
import org.junit.Before | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.robolectric.RobolectricTestRunner | ||
import org.robolectric.annotation.Config | ||
import retrofit2.Retrofit | ||
import retrofit2.converter.gson.GsonConverterFactory | ||
import javax.inject.Inject | ||
|
||
@HiltAndroidTest | ||
@RunWith(RobolectricTestRunner::class) | ||
@Config(application = HiltTestApplication::class) | ||
class ObservationRepositoryTest { | ||
lateinit var mockWebServer: MockWebServer | ||
lateinit var observationApi: RestApi | ||
|
||
@get:Rule | ||
var hiltRule = HiltAndroidRule(this) | ||
|
||
val context: Context = mockk(relaxed = true) | ||
val appContext: Context = mockk(relaxed = true) | ||
val appDatabase: AppDatabase = mockk() | ||
val observationRoomDAO: ObservationRoomDAO = mockk(relaxed = true) | ||
|
||
@Before | ||
fun init() { | ||
every { context.getApplicationContext() } returns appContext | ||
mockkStatic(OpenmrsAndroid::class) | ||
every { OpenmrsAndroid.getInstance() } returns context | ||
mockkStatic(AppDatabase::class) | ||
every { AppDatabase.getDatabase(appContext) } returns appDatabase | ||
every { appDatabase.observationRoomDAO() } returns observationRoomDAO | ||
|
||
mockkStatic(NetworkUtils::class) | ||
every { NetworkUtils.isOnline() } returns true | ||
|
||
mockWebServer = MockWebServer() | ||
mockWebServer.start() | ||
|
||
val gsonBuilder = GsonBuilder() | ||
val myGson = gsonBuilder | ||
.excludeFieldsWithoutExposeAnnotation() | ||
.create() | ||
|
||
val retrofit = Retrofit.Builder() | ||
.baseUrl(mockWebServer.url("/")) | ||
.addConverterFactory(GsonConverterFactory.create(myGson)) | ||
.build() | ||
|
||
observationApi = retrofit.create(RestApi::class.java) | ||
hiltRule.inject() | ||
} | ||
|
||
@After | ||
fun tearDown() { | ||
clearAllMocks() | ||
mockWebServer.shutdown() | ||
} | ||
|
||
@Inject | ||
lateinit var observationRepository: ObservationRepository | ||
|
||
@Test | ||
fun `createObservationFromLocal success return Observation`(){ | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") | ||
|
||
observationRepository.restApi = observationApi | ||
|
||
val observationEntity: ObservationEntity = mockk(relaxed = true) | ||
val observationToPost = Observation() | ||
|
||
mockkStatic(AppDatabaseHelper::class) | ||
every { AppDatabaseHelper.convert(observationEntity) } returns observationToPost | ||
|
||
val observation = observationRepository.createObservationFromLocal(observationEntity).toBlocking().first() | ||
|
||
assertEquals(observation.uuid, "12345") | ||
assertEquals(observation.display, "obsDisplay") | ||
assertEquals(observation.value, "120/80") | ||
assertEquals(observation.obsDatetime, "2023-07-27 12:34:56") | ||
assertEquals(observation.accessionNumber, 987) | ||
} | ||
|
||
@Test | ||
fun `getObservationByUuid success return Observation`(){ | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") | ||
|
||
observationRepository.restApi = observationApi | ||
val testUuid = "12345" | ||
val observation = observationRepository.getObservationByUuid(testUuid).toBlocking().first() | ||
|
||
assertEquals(observation.uuid, "12345") | ||
assertEquals(observation.display, "obsDisplay") | ||
assertEquals(observation.value, "120/80") | ||
assertEquals(observation.obsDatetime, "2023-07-27 12:34:56") | ||
assertEquals(observation.accessionNumber, 987) | ||
} | ||
|
||
@Test | ||
fun `getAllObservationResourcesByPatientUuid success return List of Resources`(){ | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationResources-Get.json") | ||
|
||
observationRepository.restApi = observationApi | ||
val patientUuid = "070f0120-0283-4858-885d-a20d967729cf" | ||
val observation = observationRepository.getAllObservationResourcesByPatientUuid(patientUuid).toBlocking().first().get(1) | ||
|
||
assertEquals(observation.uuid, "99a0c42b-d50e-4ae3-b826-d1959c737e74") | ||
assertEquals(observation.display, "Visit Diagnoses: Primary, Confirmed diagnosis, Disease of bone and joint") | ||
} | ||
|
||
@Test | ||
fun `getAllObservationResourcesByEncounterUuid success return List of Resources`(){ | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationResources-Get.json") | ||
|
||
observationRepository.restApi = observationApi | ||
val encounterUuid = "11c22e25-f9f8-4c79-b384-1da39ee7d5d2" | ||
val observation = observationRepository.getAllObservationResourcesByEncounterUuid(encounterUuid).toBlocking().first().get(2) | ||
|
||
assertEquals(observation.uuid, "33c83406-cd64-4c04-9506-7bdf754570a3") | ||
assertEquals(observation.display, "Diagnosis order: Primary") | ||
} | ||
|
||
@Test | ||
fun `getAllObservationsByPatientUuidAndSaveLocally success return List of Observations`(){ | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationResources-Get.json") | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") | ||
|
||
observationRepository.restApi = observationApi | ||
val patientUuid = "070f0120-0283-4858-885d-a20d967729cf" | ||
val observation = observationRepository.getAllObservationsByPatientUuidAndSaveLocally(patientUuid).toBlocking().first().get(0) | ||
|
||
assertEquals(observation.uuid, "12345") | ||
assertEquals(observation.display, "obsDisplay") | ||
} | ||
|
||
@Test | ||
fun `getAllObservationsByEncounterUuidAndSaveLocally success return List of Observations`(){ | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationResources-Get.json") | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") | ||
enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") | ||
|
||
observationRepository.restApi = observationApi | ||
val encounterUuid = "11c22e25-f9f8-4c79-b384-1da39ee7d5d2" | ||
val observation = observationRepository.getAllObservationsByPatientUuidAndSaveLocally(encounterUuid).toBlocking().first().get(0) | ||
|
||
assertEquals(observation.uuid, "12345") | ||
assertEquals(observation.display, "obsDisplay") | ||
} | ||
|
||
fun enqueueMockResponse(fileName: String) { | ||
javaClass.classLoader?.let { | ||
val inputStream = it.getResourceAsStream(fileName) | ||
val source = inputStream.source().buffer() | ||
val mockResponse = MockResponse() | ||
mockResponse.setBody(source.readString(Charsets.UTF_8)) | ||
mockResponse.setResponseCode(200) | ||
mockWebServer.enqueue(mockResponse) | ||
} | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
...dk/src/test/resources/mocked_responses/ObservationRepository/ObservationPost-success.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
{ | ||
"uuid": "12345", | ||
"display": "obsDisplay", | ||
"concept": { | ||
"uuid": "abc123", | ||
"display": "Blood Pressure" | ||
}, | ||
"value": "120/80", | ||
"person": { | ||
"uuid": "xyz789", | ||
"display": "John Doe" | ||
}, | ||
"obsDatetime": "2023-07-27 12:34:56", | ||
"accessionNumber": 987, | ||
"obsGroup": { | ||
"id": 56789, | ||
"concept": { | ||
"uuid": "def456", | ||
"display": "Temperature" | ||
}, | ||
"value": "98.6", | ||
"person": { | ||
"uuid": "uvw987", | ||
"display": "Jane Smith" | ||
}, | ||
"obsDatetime": "2023-07-27 13:45:30", | ||
"obsGroup": null, | ||
"valueCodedName": "Normal", | ||
"comment": "No concerns", | ||
"location": { | ||
"uuid": "mno654", | ||
"display": "Clinic A" | ||
}, | ||
"encounter": { | ||
"uuid": "pqr321", | ||
"display": "Initial Checkup" | ||
}, | ||
"voided": false, | ||
"formFieldPath": "/encounter/observations[0]/value", | ||
"formFieldNamespace": "http://example.org/form", | ||
"groupMembers": null, | ||
"order": "Labs", | ||
"status": "Final" | ||
}, | ||
"valueCodedName": "Normal", | ||
"comment": "No issues", | ||
"location": { | ||
"uuid": "mno654", | ||
"display": "Clinic A" | ||
}, | ||
"encounter": { | ||
"uuid": "pqr321", | ||
"display": "Initial Checkup" | ||
}, | ||
"voided": false, | ||
"formFieldPath": "/encounter/observations[0]/value", | ||
"formFieldNamespace": "http://example.org/form", | ||
"groupMembers": null, | ||
"order": "Labs", | ||
"status": "Final" | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we removing this subscriber and observer???
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rishabh-997 I was the one who added this code but when unit tests were failing I noticed this was a bug. I replaced it with "execute()" method which returns the immediate result of observable available.
"fun Observable.execute(): T = this.single().toBlocking().first()"
Like this I did many refactoring in my earlier written code.
Regardless the api call is still kept as an observable so that implementers of SDK can implement it in that pattern if wanted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we are blocking the thread till the execution is completed instead of handling in backgraound and wait for results?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RestApi function gives the result immediately. And the result won't emit any addition to the observale too, since there are no triggers to the restApi function except for once in this ObservationRepository function.
I guess the restApi functions were kept to return observales since It's a good practice to maintain consistency in the codebase, if the majority of codebase uses reactive programming, returning an Observable or a Flow from Retrofit API functions would align well with the overall architecture of the SDK.
So here we are just fetching the immediate result, instead of waiting for more results because there are absolutely zero changes to the result after the initial execution. So, It won't be any worth for the thread to keep waiting.