From 416a5b0506ddd626fee09182e281ccf8feaac356 Mon Sep 17 00:00:00 2001 From: paul sandjong Date: Thu, 11 Dec 2025 09:49:51 +0100 Subject: [PATCH 1/6] test claimAct.saveClaim --- claimManagement/build.gradle | 19 ++- .../openimis/imisclaims/ClaimActivity.java | 2 +- .../imisclaims/ClaimActivityTest.java | 139 ++++++++++++++++++ .../ClaimManagementActivityTest.java | 7 - 4 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java delete mode 100644 claimManagement/src/test/java/org/openimis/imisclaims/ClaimManagementActivityTest.java diff --git a/claimManagement/build.gradle b/claimManagement/build.gradle index c3a245d7..8cd81622 100644 --- a/claimManagement/build.gradle +++ b/claimManagement/build.gradle @@ -180,6 +180,12 @@ android { packagingOptions { exclude 'META-INF/DEPENDENCIES' } + + testOptions { + unitTests { + includeAndroidResources = true + } + } } apollo { @@ -193,6 +199,13 @@ apollo { ] } +tasks.withType(Test).configureEach { + testLogging { + events "passed", "skipped", "failed" + exceptionFormat "full" + showStandardStreams = false + } +} // Apply custom flavours if(file('custom-flavours.gradle').exists()){ @@ -215,9 +228,13 @@ dependencies { implementation ('com.apollographql.apollo:apollo-android-support:2.5.14'){ because("Apollo 3+ only works with Kotlin coroutines") } - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.mockito:mockito-core:5.5.0' + testImplementation 'org.robolectric:robolectric:4.11.1' + testImplementation 'androidx.test:core:1.5.0' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation 'androidx.test:core:1.5.0' implementation group: 'com.squareup.picasso', name: 'picasso', version: '2.71828' implementation group: 'net.lingala.zip4j', name: 'zip4j', version: '1.2.7' diff --git a/claimManagement/src/main/java/org/openimis/imisclaims/ClaimActivity.java b/claimManagement/src/main/java/org/openimis/imisclaims/ClaimActivity.java index e9894971..70990e8e 100644 --- a/claimManagement/src/main/java/org/openimis/imisclaims/ClaimActivity.java +++ b/claimManagement/src/main/java/org/openimis/imisclaims/ClaimActivity.java @@ -811,7 +811,7 @@ protected void confirmNewDialog(String msg) { runOnUiThread(() -> showDialog(msg, (dialog, which) -> ClearForm(), (dialog, which) -> dialog.dismiss())); } - private boolean saveClaim() { + protected boolean saveClaim() { Intent intent = getIntent(); String claimUUID; if (intent.hasExtra(EXTRA_CLAIM_UUID)) { diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java new file mode 100644 index 00000000..1255f9ba --- /dev/null +++ b/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java @@ -0,0 +1,139 @@ +package org.openimis.imisclaims; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import android.app.Activity; +import android.content.ContentValues; +import android.content.res.Resources; +import android.widget.AutoCompleteTextView; +import android.widget.CheckBox; +import android.widget.EditText; +import android.text.SpannableStringBuilder; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openimis.imisclaims.tools.StorageManager; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; + +import java.util.ArrayList; +import java.util.HashMap; + +@RunWith(RobolectricTestRunner.class) +public class ClaimActivityTest { + @Mock + Global global; + + @Mock + SQLHandler sqlHandler; + + @Mock + Activity activity; + + @Mock + Resources resources; + + @Mock + StorageManager storageManager; + + ClaimActivity claimActivity; + + @Before + public void setup() { + MockitoAnnotations.openMocks(this); + when(activity.getResources()).thenReturn(resources); + when(resources.getString(anyInt())).thenReturn("mockString"); + claimActivity = Robolectric.buildActivity(ClaimActivity.class).create().start().resume().visible().get(); + claimActivity.sqlHandler = sqlHandler; + claimActivity.global = global; + when(global.isNetworkAvailable()).thenReturn(true); // or false, it doesn't matter + } + + @Test + public void testSaveClaim_ShouldInsertInDatabase() { + EditText etHF = mock(EditText.class); + EditText etClaimAdmin = mock(EditText.class); + EditText etClaimCode = mock(EditText.class); + EditText etGuaranteeNo = mock(EditText.class); + EditText etCHFID = mock(EditText.class); + EditText etStart = mock(EditText.class); + EditText etEnd = mock(EditText.class); + + AutoCompleteTextView etDiagnosis = mock(AutoCompleteTextView.class); + AutoCompleteTextView etDiagnosis1 = mock(AutoCompleteTextView.class); + AutoCompleteTextView etDiagnosis2 = mock(AutoCompleteTextView.class); + AutoCompleteTextView etDiagnosis3 = mock(AutoCompleteTextView.class); + AutoCompleteTextView etDiagnosis4 = mock(AutoCompleteTextView.class); + + when(etHF.getText()).thenReturn(new SpannableStringBuilder("HF001")); + when(etClaimAdmin.getText()).thenReturn(new SpannableStringBuilder("ADMIN001")); + when(etClaimCode.getText()).thenReturn(new SpannableStringBuilder("CLM001")); + when(etGuaranteeNo.getText()).thenReturn(new SpannableStringBuilder("GNT001")); + when(etCHFID.getText()).thenReturn(new SpannableStringBuilder("INS12345")); + when(etStart.getText()).thenReturn(new SpannableStringBuilder("2025-12-01")); + when(etEnd.getText()).thenReturn(new SpannableStringBuilder("2025-12-09")); + when(etDiagnosis.getText()).thenReturn(new SpannableStringBuilder("A01")); + when(etDiagnosis1.getText()).thenReturn(new SpannableStringBuilder("B01")); + when(etDiagnosis2.getText()).thenReturn(new SpannableStringBuilder("C01")); + when(etDiagnosis3.getText()).thenReturn(new SpannableStringBuilder("D01")); + when(etDiagnosis4.getText()).thenReturn(new SpannableStringBuilder("E01")); + + AutoCompleteTextView etVisitType = mock(AutoCompleteTextView.class); + AutoCompleteTextView patientCondition = mock(AutoCompleteTextView.class); + when(etVisitType.getTag()).thenReturn("O"); + when(patientCondition.getTag()).thenReturn("A"); + + CheckBox etPreAuth = mock(CheckBox.class); + when(etPreAuth.isChecked()).thenReturn(false); + + claimActivity.etHealthFacility = etHF; + claimActivity.etClaimAdmin = etClaimAdmin; + claimActivity.etClaimCode = etClaimCode; + claimActivity.etGuaranteeNo = etGuaranteeNo; + claimActivity.etInsureeNumber = etCHFID; + claimActivity.etStartDate = etStart; + claimActivity.etEndDate = etEnd; + + claimActivity.etDiagnosis = etDiagnosis; + claimActivity.etDiagnosis1 = etDiagnosis1; + claimActivity.etDiagnosis2 = etDiagnosis2; + claimActivity.etDiagnosis3 = etDiagnosis3; + claimActivity.etDiagnosis4 = etDiagnosis4; + + claimActivity.etVisitType = etVisitType; + claimActivity.etPatientCondition = patientCondition; + claimActivity.etPreAuthorization = etPreAuth; + + ArrayList> fakeItems = new ArrayList<>(); + HashMap item1 = new HashMap<>(); + item1.put("Code", "ITEM001"); + item1.put("Price", "50"); + item1.put("Quantity", "1"); + fakeItems.add(item1); + claimActivity.lvItemList = fakeItems; + + ArrayList> fakeServices = new ArrayList<>(); + HashMap svc1 = new HashMap<>(); + svc1.put("Code", "SVC001"); + svc1.put("Price", "40"); + svc1.put("Quantity", "1"); + svc1.put("PackageType", "P"); + svc1.put("SubServicesItems", "[]"); + fakeServices.add(svc1); + claimActivity.lvServiceList = fakeServices; + + doNothing().when(sqlHandler).saveClaim(any(ContentValues.class), anyList(), anyList()); + + boolean result = claimActivity.saveClaim(); + + assertTrue(result); + verify(sqlHandler, times(1)) + .saveClaim(any(ContentValues.class), anyList(), anyList()); + } + +} diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/ClaimManagementActivityTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/ClaimManagementActivityTest.java deleted file mode 100644 index 31b92010..00000000 --- a/claimManagement/src/test/java/org/openimis/imisclaims/ClaimManagementActivityTest.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.openimis.imisclaims; - -import junit.framework.TestCase; - -public class ClaimManagementActivityTest extends TestCase { - -} \ No newline at end of file From 16594fbc859bfda547427c61f90fb6432967b269 Mon Sep 17 00:00:00 2001 From: paul sandjong Date: Thu, 11 Dec 2025 10:30:07 +0100 Subject: [PATCH 2/6] add pull_request on workflow --- .github/workflows/main.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c20ef1e7..c8b7d201 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,6 +6,10 @@ on: - '*' # tags: # - '!v*' + pull_request: + branches: + - '*' + types: [opened, synchronize, reopened] jobs: build: From 55d107b91cd2ec1fa21217b6b0f5459b950a103d Mon Sep 17 00:00:00 2001 From: paul sandjong Date: Thu, 8 Jan 2026 13:59:36 +0100 Subject: [PATCH 3/6] Unit tests for SynchronizeService --- .gitignore | 3 + .../imisclaims/SynchronizeService.java | 12 +- .../imisclaims/SynchronizeServiceTest.java | 201 ++++++++++++++++++ 3 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java diff --git a/.gitignore b/.gitignore index 9fb66257..47792f4d 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,9 @@ proguard/ # Log Files *.log +# Java heap dump files +*.hprof + # Android Studio Navigation editor temp files .navigation/ diff --git a/claimManagement/src/main/java/org/openimis/imisclaims/SynchronizeService.java b/claimManagement/src/main/java/org/openimis/imisclaims/SynchronizeService.java index f7593030..0619ff37 100644 --- a/claimManagement/src/main/java/org/openimis/imisclaims/SynchronizeService.java +++ b/claimManagement/src/main/java/org/openimis/imisclaims/SynchronizeService.java @@ -54,9 +54,9 @@ public class SynchronizeService extends JobIntentService { private static final String claimResponseLine = "[%s] %s"; - private Global global; - private SQLHandler sqlHandler; - private StorageManager storageManager; + protected Global global; + protected SQLHandler sqlHandler; + protected StorageManager storageManager; @Override public void onCreate() { @@ -96,7 +96,7 @@ protected void onHandleWork(@NonNull Intent intent) { } } - private void handleUploadClaims() { + protected void handleUploadClaims() { if (!global.isNetworkAvailable()) { broadcastError(getResources().getString(R.string.CheckInternet), ACTION_UPLOAD_CLAIMS); return; @@ -118,7 +118,7 @@ private void handleUploadClaims() { } } - private JSONArray processClaimResponse(List results) { + protected JSONArray processClaimResponse(List results) { JSONArray jsonResults = new JSONArray(); String date = AppInformation.DateTimeInfo.getDefaultIsoDatetimeFormatter().format(new Date()); for (PostNewClaims.Result result : results) { @@ -252,7 +252,7 @@ private Uri createClaimExportZip(ArrayList exportedClaims) { zipFile); } - private void handleGetClaimCount() { + protected void handleGetClaimCount() { JSONObject counts = sqlHandler.getClaimCounts(); int enteredCount = counts.optInt(SQLHandler.CLAIM_UPLOAD_STATUS_ENTERED, 0); diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java new file mode 100644 index 00000000..37c7eee3 --- /dev/null +++ b/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java @@ -0,0 +1,201 @@ +package org.openimis.imisclaims; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openimis.imisclaims.usecase.PostNewClaims; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import org.openimis.imisclaims.tools.StorageManager; + +import java.util.Arrays; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = 28, manifest = Config.NONE) +public class SynchronizeServiceTest { + + @Mock private Global global; + @Mock private SQLHandler sqlHandler; + @Mock private Resources resources; + @Mock private Context context; + @Mock private StorageManager storageManager; + + private SynchronizeService synchronizeService; + + @Before + public void setUp() { + MockitoAnnotations.openMocks(this); + + synchronizeService = new SynchronizeService() { + @Override + public Context getApplicationContext() { + return context; + } + + @Override + public Resources getResources() { + return resources; + } + + @Override + public void sendBroadcast(Intent intent) { + // override to avoid real broadcast and capture intents if needed + capturedIntent = intent; + } + }; + + synchronizeService.global = global; + synchronizeService.sqlHandler = sqlHandler; + synchronizeService.storageManager = storageManager; + + when(context.getResources()).thenReturn(resources); + when(resources.getString(anyInt())).thenReturn("Error message"); + } + + private Intent capturedIntent; + + // ---------------------------------- + // Tests handleUploadClaims + // ---------------------------------- + + @Test + public void handleUploadClaims_WhenNoNetwork_BroadcastsError() { + when(global.isNetworkAvailable()).thenReturn(false); + + synchronizeService.handleUploadClaims(); + + verify(global).isNetworkAvailable(); + assertNotNull(capturedIntent); + assertEquals(SynchronizeService.ACTION_SYNC_ERROR, capturedIntent.getAction()); + assertEquals("Error message", capturedIntent.getStringExtra(SynchronizeService.EXTRA_ERROR_MESSAGE)); + } + + @Test + public void handleUploadClaims_WhenNoPendingClaims_BroadcastsNoClaimError() throws JSONException { + when(global.isNetworkAvailable()).thenReturn(true); + when(sqlHandler.getAllPendingClaims()).thenReturn(new JSONArray()); + + synchronizeService.handleUploadClaims(); + + verify(sqlHandler).getAllPendingClaims(); + assertNotNull(capturedIntent); + assertEquals(SynchronizeService.ACTION_SYNC_ERROR, capturedIntent.getAction()); + assertEquals("Error message", capturedIntent.getStringExtra(SynchronizeService.EXTRA_ERROR_MESSAGE)); + } + + // Note : we can't test PostNewClaims here without refactoring + // We just test that the method doesn't crash with pending claims + @Test + public void handleUploadClaims_WithPendingClaims_DoesNotCrash() throws JSONException { + when(global.isNetworkAvailable()).thenReturn(true); + + JSONArray pendingClaims = new JSONArray(); + JSONObject claim = new JSONObject(); + claim.put("id", "claim123"); + pendingClaims.put(claim); + + when(sqlHandler.getAllPendingClaims()).thenReturn(pendingClaims); + + synchronizeService.handleUploadClaims(); + + verify(sqlHandler).getAllPendingClaims(); + } + + // ---------------------------------- + // Tests processClaimResponse + // ---------------------------------- + + @Test + public void processClaimResponse_WithRejectedClaim_UpdatesStatusToRejected() throws JSONException { + PostNewClaims.Result rejectedResult = + new PostNewClaims.Result("claim123", PostNewClaims.Result.Status.REJECTED, "Claim rejected"); + + when(sqlHandler.getClaimUUIDForCode("claim123")).thenReturn("uuid123"); + + JSONArray result = synchronizeService.processClaimResponse(Arrays.asList(rejectedResult)); + + verify(sqlHandler).insertClaimUploadStatus( + eq("uuid123"), + anyString(), + eq(SQLHandler.CLAIM_UPLOAD_STATUS_REJECTED), + isNull() + ); + + assertEquals(1, result.length()); + assertTrue(result.getString(0).contains("Claim rejected")); + } + + @Test + public void processClaimResponse_WithError_UpdatesStatusToError() throws JSONException { + PostNewClaims.Result errorResult = + new PostNewClaims.Result("claim123", PostNewClaims.Result.Status.ERROR, "Server error"); + + when(sqlHandler.getClaimUUIDForCode("claim123")).thenReturn("uuid123"); + + JSONArray result = synchronizeService.processClaimResponse(Arrays.asList(errorResult)); + + verify(sqlHandler).insertClaimUploadStatus( + eq("uuid123"), + anyString(), + eq(SQLHandler.CLAIM_UPLOAD_STATUS_ERROR), + eq("Server error") + ); + + assertEquals(1, result.length()); + } + + @Test + public void processClaimResponse_WithSuccess_UpdatesStatusToAccepted() throws JSONException { + PostNewClaims.Result successResult = + new PostNewClaims.Result("claim123", PostNewClaims.Result.Status.SUCCESS, null); + + when(sqlHandler.getClaimUUIDForCode("claim123")).thenReturn("uuid123"); + + JSONArray result = synchronizeService.processClaimResponse(Arrays.asList(successResult)); + + verify(sqlHandler).insertClaimUploadStatus( + eq("uuid123"), + anyString(), + eq(SQLHandler.CLAIM_UPLOAD_STATUS_ACCEPTED), + isNull() + ); + + assertEquals(0, result.length()); // no error message for SUCCESS + } + + // ---------------------------------- + // Test handleGetClaimCount + // ---------------------------------- + + @Test + public void handleGetClaimCount_BroadcastsCorrectCounts() throws JSONException { + JSONObject counts = new JSONObject(); + counts.put(SQLHandler.CLAIM_UPLOAD_STATUS_ENTERED, 5); + counts.put(SQLHandler.CLAIM_UPLOAD_STATUS_ACCEPTED, 3); + counts.put(SQLHandler.CLAIM_UPLOAD_STATUS_REJECTED, 2); + + when(sqlHandler.getClaimCounts()).thenReturn(counts); + + synchronizeService.handleGetClaimCount(); + + assertNotNull(capturedIntent); + assertEquals(SynchronizeService.ACTION_CLAIM_COUNT_RESULT, capturedIntent.getAction()); + assertEquals(5, capturedIntent.getIntExtra(SynchronizeService.EXTRA_CLAIM_COUNT_ENTERED, 0)); + assertEquals(3, capturedIntent.getIntExtra(SynchronizeService.EXTRA_CLAIM_COUNT_ACCEPTED, 0)); + assertEquals(2, capturedIntent.getIntExtra(SynchronizeService.EXTRA_CLAIM_COUNT_REJECTED, 0)); + } +} From 550aa56eaeda03b41d1a5c6d2eb31331c51d255c Mon Sep 17 00:00:00 2001 From: paul sandjong Date: Fri, 9 Jan 2026 12:21:09 +0100 Subject: [PATCH 4/6] Unit tests for madatory fields for saveClaim and claims restored --- .../openimis/imisclaims/ClaimActivity.java | 4 +- .../imisclaims/SynchronizeService.java | 10 +- .../imisclaims/ClaimActivityTest.java | 339 +++++++++++++----- .../imisclaims/SynchronizeServiceTest.java | 1 + 4 files changed, 261 insertions(+), 93 deletions(-) diff --git a/claimManagement/src/main/java/org/openimis/imisclaims/ClaimActivity.java b/claimManagement/src/main/java/org/openimis/imisclaims/ClaimActivity.java index 70990e8e..171629a8 100644 --- a/claimManagement/src/main/java/org/openimis/imisclaims/ClaimActivity.java +++ b/claimManagement/src/main/java/org/openimis/imisclaims/ClaimActivity.java @@ -712,7 +712,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } } - private boolean isValidData() { + protected boolean isValidData() { if (etHealthFacility.getText().length() == 0) { showValidationDialog(etHealthFacility, getResources().getString(R.string.MissingHealthFacility)); @@ -793,7 +793,7 @@ private boolean isValidData() { return true; } - private boolean isValidInsureeNumber() { + protected boolean isValidInsureeNumber() { Escape escape = new Escape(); return escape.CheckCHFID(etInsureeNumber.getText().toString()); } diff --git a/claimManagement/src/main/java/org/openimis/imisclaims/SynchronizeService.java b/claimManagement/src/main/java/org/openimis/imisclaims/SynchronizeService.java index 0619ff37..ce198bba 100644 --- a/claimManagement/src/main/java/org/openimis/imisclaims/SynchronizeService.java +++ b/claimManagement/src/main/java/org/openimis/imisclaims/SynchronizeService.java @@ -57,6 +57,7 @@ public class SynchronizeService extends JobIntentService { protected Global global; protected SQLHandler sqlHandler; protected StorageManager storageManager; + protected PostNewClaims postNewClaims; @Override public void onCreate() { @@ -66,6 +67,10 @@ public void onCreate() { storageManager = StorageManager.of(this); } + public void setPostNewClaims(PostNewClaims postNewClaims) { + this.postNewClaims = postNewClaims; + } + public static void uploadClaims(Context context) { Intent intent = new Intent(); intent.setAction(ACTION_UPLOAD_CLAIMS); @@ -109,7 +114,10 @@ protected void handleUploadClaims() { } try { - List results = new PostNewClaims().execute(PendingClaim.fromJson(claims)); + if (postNewClaims == null) { + postNewClaims = new PostNewClaims(); + } + List results = postNewClaims.execute(PendingClaim.fromJson(claims)); JSONArray claimStatus = processClaimResponse(results); broadcastSyncSuccess(claimStatus); } catch (Exception e) { diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java index 1255f9ba..79a480ee 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java @@ -1,121 +1,80 @@ package org.openimis.imisclaims; +import org.openimis.imisclaims.domain.entity.Claim; import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import android.app.Activity; import android.content.ContentValues; -import android.content.res.Resources; -import android.widget.AutoCompleteTextView; -import android.widget.CheckBox; -import android.widget.EditText; import android.text.SpannableStringBuilder; +import android.view.View; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.openimis.imisclaims.tools.StorageManager; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.HashMap; +import java.util.Date; +import java.util.List; @RunWith(RobolectricTestRunner.class) +@Config(sdk = 28, manifest = Config.NONE) public class ClaimActivityTest { - @Mock - Global global; - - @Mock - SQLHandler sqlHandler; - - @Mock - Activity activity; - - @Mock - Resources resources; - - @Mock - StorageManager storageManager; - - ClaimActivity claimActivity; + + ClaimActivity activity; @Before public void setup() { MockitoAnnotations.openMocks(this); - when(activity.getResources()).thenReturn(resources); - when(resources.getString(anyInt())).thenReturn("mockString"); - claimActivity = Robolectric.buildActivity(ClaimActivity.class).create().start().resume().visible().get(); - claimActivity.sqlHandler = sqlHandler; - claimActivity.global = global; - when(global.isNetworkAvailable()).thenReturn(true); // or false, it doesn't matter - } - @Test - public void testSaveClaim_ShouldInsertInDatabase() { - EditText etHF = mock(EditText.class); - EditText etClaimAdmin = mock(EditText.class); - EditText etClaimCode = mock(EditText.class); - EditText etGuaranteeNo = mock(EditText.class); - EditText etCHFID = mock(EditText.class); - EditText etStart = mock(EditText.class); - EditText etEnd = mock(EditText.class); - - AutoCompleteTextView etDiagnosis = mock(AutoCompleteTextView.class); - AutoCompleteTextView etDiagnosis1 = mock(AutoCompleteTextView.class); - AutoCompleteTextView etDiagnosis2 = mock(AutoCompleteTextView.class); - AutoCompleteTextView etDiagnosis3 = mock(AutoCompleteTextView.class); - AutoCompleteTextView etDiagnosis4 = mock(AutoCompleteTextView.class); - - when(etHF.getText()).thenReturn(new SpannableStringBuilder("HF001")); - when(etClaimAdmin.getText()).thenReturn(new SpannableStringBuilder("ADMIN001")); - when(etClaimCode.getText()).thenReturn(new SpannableStringBuilder("CLM001")); - when(etGuaranteeNo.getText()).thenReturn(new SpannableStringBuilder("GNT001")); - when(etCHFID.getText()).thenReturn(new SpannableStringBuilder("INS12345")); - when(etStart.getText()).thenReturn(new SpannableStringBuilder("2025-12-01")); - when(etEnd.getText()).thenReturn(new SpannableStringBuilder("2025-12-09")); - when(etDiagnosis.getText()).thenReturn(new SpannableStringBuilder("A01")); - when(etDiagnosis1.getText()).thenReturn(new SpannableStringBuilder("B01")); - when(etDiagnosis2.getText()).thenReturn(new SpannableStringBuilder("C01")); - when(etDiagnosis3.getText()).thenReturn(new SpannableStringBuilder("D01")); - when(etDiagnosis4.getText()).thenReturn(new SpannableStringBuilder("E01")); - - AutoCompleteTextView etVisitType = mock(AutoCompleteTextView.class); - AutoCompleteTextView patientCondition = mock(AutoCompleteTextView.class); - when(etVisitType.getTag()).thenReturn("O"); - when(patientCondition.getTag()).thenReturn("A"); - - CheckBox etPreAuth = mock(CheckBox.class); - when(etPreAuth.isChecked()).thenReturn(false); - - claimActivity.etHealthFacility = etHF; - claimActivity.etClaimAdmin = etClaimAdmin; - claimActivity.etClaimCode = etClaimCode; - claimActivity.etGuaranteeNo = etGuaranteeNo; - claimActivity.etInsureeNumber = etCHFID; - claimActivity.etStartDate = etStart; - claimActivity.etEndDate = etEnd; - - claimActivity.etDiagnosis = etDiagnosis; - claimActivity.etDiagnosis1 = etDiagnosis1; - claimActivity.etDiagnosis2 = etDiagnosis2; - claimActivity.etDiagnosis3 = etDiagnosis3; - claimActivity.etDiagnosis4 = etDiagnosis4; - - claimActivity.etVisitType = etVisitType; - claimActivity.etPatientCondition = patientCondition; - claimActivity.etPreAuthorization = etPreAuth; + activity = Robolectric.buildActivity(ClaimActivity.class) + .create() + .start() + .resume() + .get(); + + // Inject mocks + activity.sqlHandler = mock(SQLHandler.class); + activity.global = mock(Global.class); + + when(activity.global.isNetworkAvailable()).thenReturn(true); + when(activity.sqlHandler.getAdjustability(anyString())).thenReturn("M"); + + // initialize static lists + ClaimActivity.lvItemList = new ArrayList<>(); + ClaimActivity.lvServiceList = new ArrayList<>(); + // set mandatory fields + activity.etHealthFacility.setText("HF001"); + activity.etClaimAdmin.setText("ADMIN001"); + activity.etClaimCode.setText("CLM001"); + activity.etInsureeNumber.setText("CHF12345"); + activity.etStartDate.setText("2025-01-01"); + activity.etEndDate.setText("2025-01-02"); + activity.etDiagnosis.setText("A01"); + + activity.etVisitType.setText("Other"); + activity.etVisitType.setTag("O"); + + activity.etPatientCondition.setText("Healed"); + activity.etPatientCondition.setTag("H"); + + activity.tvItemTotal.setText("1"); + activity.tvServiceTotal.setText("0"); + + // add fake items and services to avoid MissingClaim ArrayList> fakeItems = new ArrayList<>(); HashMap item1 = new HashMap<>(); item1.put("Code", "ITEM001"); item1.put("Price", "50"); item1.put("Quantity", "1"); fakeItems.add(item1); - claimActivity.lvItemList = fakeItems; + ClaimActivity.lvItemList = fakeItems; ArrayList> fakeServices = new ArrayList<>(); HashMap svc1 = new HashMap<>(); @@ -125,15 +84,215 @@ public void testSaveClaim_ShouldInsertInDatabase() { svc1.put("PackageType", "P"); svc1.put("SubServicesItems", "[]"); fakeServices.add(svc1); - claimActivity.lvServiceList = fakeServices; + ClaimActivity.lvServiceList = fakeServices; + } - doNothing().when(sqlHandler).saveClaim(any(ContentValues.class), anyList(), anyList()); + /* ===================================================== + isValidData() + ===================================================== */ + + @Test + public void isValidData_AllValid_ReturnsTrue() { + boolean result = activity.isValidData(); + assertTrue(result); + } + + @Test + public void isValidData_MissingHealthFacility_ReturnsFalse() { + activity.etHealthFacility.setText(""); + + boolean result = activity.isValidData(); + + assertFalse(result); + } + + @Test + public void isValidData_MissingClaimAdmin_WhenMandatory_ReturnsFalse() { + when(activity.sqlHandler.getAdjustability("ClaimAdministrator")) + .thenReturn("M"); - boolean result = claimActivity.saveClaim(); + activity.etClaimAdmin.setText(""); + + boolean result = activity.isValidData(); + + assertFalse(result); + } + + @Test + public void isValidData_MissingClaimCode_ReturnsFalse() { + activity.etClaimCode.setText(""); + + boolean result = activity.isValidData(); + + assertFalse(result); + } + + @Test + public void isValidData_MissingInsureeNumber_ReturnsFalse() { + activity.etInsureeNumber.setText(""); + + boolean result = activity.isValidData(); + + assertFalse(result); + } + + @Test + public void isValidData_InvalidInsureeNumber_ReturnsFalse() { + activity.etInsureeNumber.setText(""); + Escape escape = mock(Escape.class); + when(escape.CheckCHFID(anyString())).thenReturn(false); // Mock Escape.CheckCHFID to return false + boolean result = activity.isValidData(); + + assertFalse(result); + } + + @Test + public void isValidData_MissingStartDate_ReturnsFalse() { + activity.etStartDate.setText(""); + + boolean result = activity.isValidData(); + + assertFalse(result); + } + + @Test + public void isValidData_MissingEndDate_ReturnsFalse() { + activity.etEndDate.setText(""); + + boolean result = activity.isValidData(); + + assertFalse(result); + } + + @Test + public void isValidData_MissingDiagnosis_ReturnsFalse() { + activity.etDiagnosis.setText(""); + + boolean result = activity.isValidData(); + + assertFalse(result); + } + + @Test + public void isValidData_MissingVisitType_ReturnsFalse() { + activity.etVisitType.setText(""); + activity.etVisitType.setTag(""); + + boolean result = activity.isValidData(); + + assertFalse(result); + } + + @Test + public void isValidData_NoItemsAndServices_ReturnsFalse() { + ClaimActivity.lvItemList.clear(); + ClaimActivity.lvServiceList.clear(); + activity.tvItemTotal.setText("0"); + activity.tvServiceTotal.setText("0"); + + boolean result = activity.isValidData(); + + assertFalse(result); + } + + /* ===================================================== + saveClaim() + ===================================================== */ + + @Test + public void saveClaim_ValidData_CallsSqlHandlerAndReturnsTrue() { + doNothing().when(activity.sqlHandler) + .saveClaim(any(ContentValues.class), anyList(), anyList()); + + boolean result = activity.saveClaim(); assertTrue(result); - verify(sqlHandler, times(1)) + verify(activity.sqlHandler, times(1)) .saveClaim(any(ContentValues.class), anyList(), anyList()); } + @Test + public void saveClaim_ContentValuesContainExpectedData() { + doNothing().when(activity.sqlHandler) + .saveClaim(any(ContentValues.class), anyList(), anyList()); + + activity.saveClaim(); + + verify(activity.sqlHandler).saveClaim( + argThat(cv -> + cv.getAsString("HFCode").equals("HF001") && + cv.getAsString("ClaimCode").equals("CLM001") && + cv.getAsString("VisitType").equals("O") && + cv.getAsInteger("PreAuthorization") == 0 + ), + anyList(), + anyList() + ); + } + + /* ===================================================== + fillClaimFromRestore() + ===================================================== */ + + @Test + public void fillClaimFromRestore_FillsAllFieldsCorrectly() throws Exception { + // Build a complete claim + Claim.Medication med = new Claim.Medication( + "MED1", "Paracetamol", 50.0, "USD", "2", null, null, null, null, null + ); + + Claim.Service svc = new Claim.Service( + "SVC1", "Consultation", 100.0, "USD", "1", null, null, null, null, null + ); + + Claim claim = new Claim( + "UUID123", + "HF001", + "Health Facility", + "INS001", + "John Doe", + "CLM123", + new Date(), + new Date(), + new Date(), + "E", + Claim.Status.ENTERED, + "A01", + "B02", + null, + null, + null, + 150.0, + 150.0, + null, + null, + "G001", + List.of(svc), + List.of(med) + ); + + // Mock global for the method + when(activity.global.getOfficerCode()).thenReturn("ADMIN001"); + when(activity.global.getOfficerHealthFacility()).thenReturn("HF001"); + + // Call the private method using reflection + java.lang.reflect.Method method = ClaimActivity.class + .getDeclaredMethod("fillClaimFromRestore", Claim.class); + method.setAccessible(true); + method.invoke(activity, claim); + + // Verify that UI fields were filled correctly + assertEquals("@CLM123", activity.etClaimCode.getText().toString()); // @ is the mocks resource string to replace "Restored" + assertEquals("ADMIN001", activity.etClaimAdmin.getText().toString()); + assertEquals("HF001", activity.etHealthFacility.getText().toString()); + assertEquals("G001", activity.etGuaranteeNo.getText().toString()); + assertEquals("", activity.etInsureeNumber.getText().toString()); // because Status != REJECTED + assertEquals("", activity.etDiagnosis.getText().toString()); + + // Verify that the lists were populated + assertEquals(1, ClaimActivity.lvItemList.size()); + assertEquals(1, ClaimActivity.lvServiceList.size()); + assertEquals(2, activity.TotalItemService); // 1 item + 1 service + } + } diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java index 37c7eee3..a0ee116e 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java @@ -81,6 +81,7 @@ public void handleUploadClaims_WhenNoNetwork_BroadcastsError() { verify(global).isNetworkAvailable(); assertNotNull(capturedIntent); assertEquals(SynchronizeService.ACTION_SYNC_ERROR, capturedIntent.getAction()); + System.out.println("Error Message: " + capturedIntent.getStringExtra(SynchronizeService.EXTRA_ERROR_MESSAGE)); assertEquals("Error message", capturedIntent.getStringExtra(SynchronizeService.EXTRA_ERROR_MESSAGE)); } From b9753e2fa92e2c9b230a4eb250e6b335916bca3f Mon Sep 17 00:00:00 2001 From: paul sandjong Date: Fri, 9 Jan 2026 13:08:31 +0100 Subject: [PATCH 5/6] fix java heap space on runners --- .../test/java/org/openimis/imisclaims/ClaimActivityTest.java | 1 - .../java/org/openimis/imisclaims/SynchronizeServiceTest.java | 1 - gradle.properties | 4 +++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java index 79a480ee..f6a90b1d 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/ClaimActivityTest.java @@ -23,7 +23,6 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) -@Config(sdk = 28, manifest = Config.NONE) public class ClaimActivityTest { ClaimActivity activity; diff --git a/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java b/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java index a0ee116e..4f154a09 100644 --- a/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java +++ b/claimManagement/src/test/java/org/openimis/imisclaims/SynchronizeServiceTest.java @@ -25,7 +25,6 @@ import java.util.Arrays; @RunWith(RobolectricTestRunner.class) -@Config(sdk = 28, manifest = Config.NONE) public class SynchronizeServiceTest { @Mock private Global global; diff --git a/gradle.properties b/gradle.properties index 5465fec0..da25f989 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,4 @@ android.enableJetifier=true -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=true +org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.parallel=false \ No newline at end of file From ffdd115ce60616d44150ba8d4be4881126c41fc9 Mon Sep 17 00:00:00 2001 From: Blue-B-code Date: Fri, 9 Jan 2026 16:50:24 +0100 Subject: [PATCH 6/6] Remove unused Gradle properties --- gradle.properties | 2 -- 1 file changed, 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index da25f989..dbb7bf70 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,2 @@ android.enableJetifier=true android.useAndroidX=true -org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -org.gradle.parallel=false \ No newline at end of file