From d350286dcbec313602fbf83d44136f7ddb5fc2b2 Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 11:45:26 +0330 Subject: [PATCH 01/13] add android-ci.yml file for triggering the CI pipeline --- .github/workflows/android-ci.yml | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/android-ci.yml diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml new file mode 100644 index 0000000..5c2fcdc --- /dev/null +++ b/.github/workflows/android-ci.yml @@ -0,0 +1,35 @@ +name: Android CI + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build + + - name: Run tests + run: ./gradlew test + + - name: Run lint checks + run: ./gradlew lint \ No newline at end of file From 17cb86677e1c78986a7209cf911e62e2c9fa35cb Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 11:55:26 +0330 Subject: [PATCH 02/13] add lint, unit test and instrumentation-test jobs --- .github/workflows/android-ci.yml | 50 +++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 5c2fcdc..f449374 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -9,27 +9,49 @@ on: - master jobs: - build: + lint: runs-on: ubuntu-latest - steps: - - name: Checkout code + - name: Checkout the code uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v2 + - name: Upload html test report + uses: actions/upload-artifact@v2 with: - distribution: 'adopt' - java-version: '11' - - - name: Grant execute permission for gradlew - run: chmod +x gradlew + name: lint.html + path: app/build/reports/lint-results-debug.html - - name: Build with Gradle - run: ./gradlew build + unit-test: + needs: [lint] + runs-on: ubuntu-latest + steps: + - name: Checkout the code + uses: actions/checkout@v2 - name: Run tests run: ./gradlew test - - name: Run lint checks - run: ./gradlew lint \ No newline at end of file + - name: Upload test report + uses: actions/upload-artifact@v2 + with: + name: unit_test_report + path: app/build/reports/tests/testDebugUnitTest/ + + instrumentation-test: + needs: [unit-test] + runs-on: macos-latest + steps: + - name: Checkout the code + uses: actions/checkout@v2 + + - name: Run espresso tests + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 29 + script: ./gradlew connectedCheck + + - name: Upload test report + uses: actions/upload-artifact@v2 + with: + name: instrumentation_test_report + path: app/build/reports/androidTests/connected/ \ No newline at end of file From 2b56180d7dcb34fc327178bfbdbaeace6b726f58 Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 12:05:23 +0330 Subject: [PATCH 03/13] upgrade artifact actions from v2 to v4 --- .github/workflows/android-ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index f449374..575ddcd 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -13,10 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Upload html test report - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: lint.html path: app/build/reports/lint-results-debug.html @@ -26,13 +26,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Run tests run: ./gradlew test - name: Upload test report - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: unit_test_report path: app/build/reports/tests/testDebugUnitTest/ @@ -42,7 +42,7 @@ jobs: runs-on: macos-latest steps: - name: Checkout the code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Run espresso tests uses: reactivecircus/android-emulator-runner@v2 @@ -51,7 +51,7 @@ jobs: script: ./gradlew connectedCheck - name: Upload test report - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: instrumentation_test_report path: app/build/reports/androidTests/connected/ \ No newline at end of file From 21394b93bdfe2ba7663897b8a96c864820bdf2b3 Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 12:44:04 +0330 Subject: [PATCH 04/13] change lint report file path --- .github/workflows/android-ci.yml | 73 ++++++++++++++++---------------- domain/build.gradle.kts | 4 ++ 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 575ddcd..4a79a19 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -19,39 +19,40 @@ jobs: uses: actions/upload-artifact@v4 with: name: lint.html - path: app/build/reports/lint-results-debug.html - - unit-test: - needs: [lint] - runs-on: ubuntu-latest - steps: - - name: Checkout the code - uses: actions/checkout@v4 - - - name: Run tests - run: ./gradlew test - - - name: Upload test report - uses: actions/upload-artifact@v4 - with: - name: unit_test_report - path: app/build/reports/tests/testDebugUnitTest/ - - instrumentation-test: - needs: [unit-test] - runs-on: macos-latest - steps: - - name: Checkout the code - uses: actions/checkout@v4 - - - name: Run espresso tests - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: 29 - script: ./gradlew connectedCheck - - - name: Upload test report - uses: actions/upload-artifact@v4 - with: - name: instrumentation_test_report - path: app/build/reports/androidTests/connected/ \ No newline at end of file + path: presentation/build/reports/lint-results-debug.html + +# unit-test: +# needs: [lint] +# runs-on: ubuntu-latest +# steps: +# - name: Checkout the code +# uses: actions/checkout@v4 +# +# - name: Run tests +# run: ./gradlew test +# +# - name: Upload test report +# uses: actions/upload-artifact@v4 +# with: +# name: unit_test_report +# path: app/build/reports/tests/testDebugUnitTest/ + +# instrumentation-test: +# needs: [unit-test] +# runs-on: macos-latest +# steps: +# - name: Checkout the code +# uses: actions/checkout@v4 +# +# - name: Run espresso tests +# uses: reactivecircus/android-emulator-runner@v2 +# with: +# api-level: 35 +# script: ./gradlew connectedCheck +# startup-timeout: 5m +# +# - name: Upload test report +# uses: actions/upload-artifact@v4 +# with: +# name: instrumentation_test_report +# path: app/build/reports/androidTests/connected/ \ No newline at end of file diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index fca09bf..9dfe08e 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -6,6 +6,10 @@ plugins { java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 + + toolchain { + languageVersion.set(JavaLanguageVersion.of(JavaVersion.VERSION_17.toString())) + } } dependencies { From d78add2747d355c4cf01665a839182174e66a321 Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 12:48:26 +0330 Subject: [PATCH 05/13] change lint job --- .github/workflows/android-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 4a79a19..19409b3 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -12,11 +12,11 @@ jobs: lint: runs-on: ubuntu-latest steps: - - name: Checkout the code - uses: actions/checkout@v4 + - name: Lint + uses: ./gradlew lint - - name: Upload html test report - uses: actions/upload-artifact@v4 + - name: Check and report lint results + uses: hidakatsuya/action-report-android-lint@v1.2.0 with: name: lint.html path: presentation/build/reports/lint-results-debug.html From bd26e3d26b84e1a24b6486ca66f73004ddea623a Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 12:48:26 +0330 Subject: [PATCH 06/13] change lint job --- .github/workflows/android-ci.yml | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 4a79a19..bd78e5c 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -9,17 +9,27 @@ on: - master jobs: - lint: + android-lint: runs-on: ubuntu-latest - steps: - - name: Checkout the code - uses: actions/checkout@v4 - - name: Upload html test report - uses: actions/upload-artifact@v4 + steps: + - uses: actions/checkout@v3 + - name: set up JDK 17 + uses: actions/setup-java@v3 with: - name: lint.html - path: presentation/build/reports/lint-results-debug.html + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Run Lint + run: ./gradlew lint + continue-on-error: false + +# - name: Check and report lint results +# uses: hidakatsuya/action-report-android-lint@v1.2.0 +# with: +# name: lint.html +# path: presentation/build/reports/lint-results-debug.html # unit-test: # needs: [lint] From 456e0d7379a04fa72492fa75cfabe36c1bcf3889 Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 13:39:09 +0330 Subject: [PATCH 07/13] fix lint issues --- .../drawable-v24/ic_launcher_foreground.xml | 30 ---- .../res/drawable/ic_launcher_background.xml | 170 ------------------ .../res/mipmap-anydpi-v26/ic_launcher.xml | 8 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 8 +- app/src/main/res/values/colors.xml | 7 - gradle/libs.versions.toml | 2 +- 6 files changed, 11 insertions(+), 214 deletions(-) delete mode 100644 app/src/main/res/drawable-v24/ic_launcher_foreground.xml delete mode 100644 app/src/main/res/drawable/ic_launcher_background.xml diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 036d09b..4a4b17f 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,7 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 036d09b..4a4b17f 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,7 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f8c6127..55344e5 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,10 +1,3 @@ - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 69e7dcf..ff40235 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ agp = "8.7.3" kotlin = "2.1.0" coreKtx = "1.15.0" junit = "4.13.2" -mockito = "5.12.0" +mockito = "5.14.2" junitVersion = "1.2.1" espressoCore = "3.6.1" lifecycleRuntimeKtx = "2.8.7" From bc1ffc619a9b963671aef4220f1fc450f2019bab Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 14:54:28 +0330 Subject: [PATCH 08/13] add unit test job --- .github/workflows/android-ci.yml | 19 ++ .../local/database/room/dao/SurveyDaoTest.kt | 260 +++++++++--------- .../room/dao/SurveyRemoteKeyDaoTest.kt | 234 ++++++++-------- gradle/libs.versions.toml | 2 +- presentation/build.gradle.kts | 7 + 5 files changed, 274 insertions(+), 248 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index a3c26c9..addfca3 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -24,6 +24,25 @@ jobs: name: lint-report path: presentation/build/reports/lint-results-debug.html + unit-test: + runs-on: ubuntu-latest + strategy: + matrix: + flavor: [development, staging, production] + buildType: [debug, release] + steps: + - name: Checkout the code + uses: actions/checkout@v4 + + - name: Run unit tests for ${{ matrix.flavor }} ${{ matrix.buildType }} + run: ./gradlew test${{ matrix.flavor.capitalize() }}${{ matrix.buildType.capitalize() }}UnitTest + + - name: Upload test report for ${{ matrix.flavor }} ${{ matrix.buildType }} + uses: actions/upload-artifact@v4 + with: + name: unit-test-report-${{ matrix.flavor }}-${{ matrix.buildType }} + path: app/build/reports/tests/test${{ matrix.flavor.capitalize() }}${{ matrix.buildType.capitalize() }}UnitTest/ + # unit-test: # needs: [lint] # runs-on: ubuntu-latest diff --git a/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt b/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt index d60174f..0705767 100644 --- a/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt +++ b/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt @@ -1,130 +1,130 @@ -package com.mkdev.data.datasource.local.database.room.dao - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.paging.PagingSource -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.mkdev.data.datasource.local.database.room.NimbleRoomDatabase -import com.mkdev.data.datasource.local.database.room.entity.SurveyEntity -import com.mkdev.data.factory.SurveyEntityFactory -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.TestDispatcher -import kotlinx.coroutines.test.runTest -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 - -@OptIn(ExperimentalCoroutinesApi::class) -@RunWith(AndroidJUnit4::class) -class SurveyDaoTest { - - @get:Rule - var instantExecutorRule = InstantTaskExecutorRule() - - private lateinit var database: NimbleRoomDatabase - private lateinit var surveyDao: SurveyDao - private lateinit var testDispatcher:TestDispatcher - - @Before - fun setUp() { - database = Room.inMemoryDatabaseBuilder( - ApplicationProvider.getApplicationContext(), - NimbleRoomDatabase::class.java - ).allowMainThreadQueries().build() - surveyDao = database.surveyDao() - - testDispatcher = StandardTestDispatcher() - } - - @After - fun tearDown() { - database.close() - } - - @Test - fun `insertAll should insert surveys into database`() = runTest(testDispatcher) { - // Given - val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) - - // When - surveyDao.insertAll(surveys) - - // Then - val insertedSurvey1 = surveyDao.getById(surveys[0].id) - val insertedSurvey2 = surveyDao.getById(surveys[1].id) - - assertEquals(surveys[0], insertedSurvey1) - assertEquals(surveys[1], insertedSurvey2) - } - - @Test - fun `getByPaging should return paging source of surveys`() = runTest(testDispatcher) { - // Given - val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) - surveyDao.insertAll(surveys) - - // When - val pagingSource = surveyDao.getByPaging() - val loadResult = pagingSource.load( - PagingSource.LoadParams.Refresh( - key = null, - loadSize = 2, - placeholdersEnabled = false - ) - ) - - // Then - assertEquals(surveys, (loadResult as PagingSource.LoadResult.Page).data) - } - - @Test - fun `getById should return survey by id`() = runTest(testDispatcher) { - // Given - val survey = SurveyEntityFactory.createSurveyEntity() - surveyDao.insertAll(listOf(survey)) - - // When - val retrievedSurvey = surveyDao.getById(survey.id) - - // Then - assertEquals(survey, retrievedSurvey) - } - - @Test - fun `getById should return null when survey not found`() = runTest(testDispatcher) { - // Given - val nonexistentId = "nonexistent_id" - - // When - val retrievedSurvey = surveyDao.getById(nonexistentId) - - // Then - assertEquals(null, retrievedSurvey) - } - - @Test - fun `clearAll should clear all surveys from database`() = runTest(testDispatcher) { - // Given - val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) - surveyDao.insertAll(surveys) - - // When - surveyDao.clearAll() - - // Then - val pagingSource = surveyDao.getByPaging() - val loadResult = pagingSource.load( - PagingSource.LoadParams.Refresh( - key = null, - loadSize = 2, - placeholdersEnabled = false - ) - ) - assertEquals(emptyList(), (loadResult as PagingSource.LoadResult.Page).data) - } -} \ No newline at end of file +//package com.mkdev.data.datasource.local.database.room.dao +// +//import androidx.arch.core.executor.testing.InstantTaskExecutorRule +//import androidx.paging.PagingSource +//import androidx.room.Room +//import androidx.test.core.app.ApplicationProvider +//import androidx.test.ext.junit.runners.AndroidJUnit4 +//import com.mkdev.data.datasource.local.database.room.NimbleRoomDatabase +//import com.mkdev.data.datasource.local.database.room.entity.SurveyEntity +//import com.mkdev.data.factory.SurveyEntityFactory +//import kotlinx.coroutines.test.StandardTestDispatcher +//import kotlinx.coroutines.test.TestDispatcher +//import kotlinx.coroutines.test.runTest +//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.annotation.Config +// +//@RunWith(AndroidJUnit4::class) +//@Config(manifest= Config.NONE) +//class SurveyDaoTest { +// +// @get:Rule +// var instantExecutorRule = InstantTaskExecutorRule() +// +// private lateinit var database: NimbleRoomDatabase +// private lateinit var surveyDao: SurveyDao +// private lateinit var testDispatcher:TestDispatcher +// +// @Before +// fun setUp() { +// database = Room.inMemoryDatabaseBuilder( +// ApplicationProvider.getApplicationContext(), +// NimbleRoomDatabase::class.java +// ).allowMainThreadQueries().build() +// surveyDao = database.surveyDao() +// +// testDispatcher = StandardTestDispatcher() +// } +// +// @After +// fun tearDown() { +// database.close() +// } +// +// @Test +// fun `insertAll should insert surveys into database`() = runTest { +// // Given +// val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) +// +// // When +// surveyDao.insertAll(surveys) +// +// // Then +// val insertedSurvey1 = surveyDao.getById(surveys[0].id) +// val insertedSurvey2 = surveyDao.getById(surveys[1].id) +// +// assertEquals(surveys[0], insertedSurvey1) +// assertEquals(surveys[1], insertedSurvey2) +// } +// +// @Test +// fun `getByPaging should return paging source of surveys`() = runTest(testDispatcher) { +// // Given +// val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) +// surveyDao.insertAll(surveys) +// +// // When +// val pagingSource = surveyDao.getByPaging() +// val loadResult = pagingSource.load( +// PagingSource.LoadParams.Refresh( +// key = null, +// loadSize = 2, +// placeholdersEnabled = false +// ) +// ) +// +// // Then +// assertEquals(surveys, (loadResult as PagingSource.LoadResult.Page).data) +// } +// +// @Test +// fun `getById should return survey by id`() = runTest { +// // Given +// val survey = SurveyEntityFactory.createSurveyEntity() +// surveyDao.insertAll(listOf(survey)) +// +// // When +// val retrievedSurvey = surveyDao.getById(survey.id) +// +// // Then +// assertEquals(survey, retrievedSurvey) +// } +// +// @Test +// fun `getById should return null when survey not found`() = runTest(testDispatcher) { +// // Given +// val nonexistentId = "nonexistent_id" +// +// // When +// val retrievedSurvey = surveyDao.getById(nonexistentId) +// +// // Then +// assertEquals(null, retrievedSurvey) +// } +// +// @Test +// fun `clearAll should clear all surveys from database`() = runTest(testDispatcher) { +// // Given +// val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) +// surveyDao.insertAll(surveys) +// +// // When +// surveyDao.clearAll() +// +// // Then +// val pagingSource = surveyDao.getByPaging() +// val loadResult = pagingSource.load( +// PagingSource.LoadParams.Refresh( +// key = null, +// loadSize = 2, +// placeholdersEnabled = false +// ) +// ) +// assertEquals(emptyList(), (loadResult as PagingSource.LoadResult.Page).data) +// } +//} \ No newline at end of file diff --git a/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt b/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt index b860296..3158e32 100644 --- a/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt +++ b/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt @@ -1,117 +1,117 @@ -package com.mkdev.data.datasource.local.database.room.dao - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.mkdev.data.datasource.local.database.room.NimbleRoomDatabase -import com.mkdev.data.datasource.local.database.room.entity.SurveyRemoteKeyEntity -import com.mkdev.data.factory.SurveyRemoteKeyEntityFactory -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.runTest -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class SurveyRemoteKeyDaoTest { - - private lateinit var database: NimbleRoomDatabase - private lateinit var surveyRemoteKeyDao: SurveyRemoteKeyDao - private val testDispatcher = StandardTestDispatcher() - - @Before - fun setUp() { - database = Room.inMemoryDatabaseBuilder( - ApplicationProvider.getApplicationContext(), - NimbleRoomDatabase::class.java - ).allowMainThreadQueries().build() - surveyRemoteKeyDao = database.surveyRemoteKeyDao() - } - - @After - fun tearDown() { - database.close() - } - - @Test - fun `insertAll should insert remote keys into database`() = runTest(testDispatcher) { - // Given - val remoteKeys = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntityList(count = 2) - - // When - surveyRemoteKeyDao.insertAll(remoteKeys) - - // Then - val allRemoteKeys = surveyRemoteKeyDao.getAll() - Assert.assertEquals(remoteKeys, allRemoteKeys) - } - - @Test - fun `insert should insert a single remote key into database`() = runTest(testDispatcher) { - // Given - val remoteKey = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() - - // When - surveyRemoteKeyDao.insert(remoteKey) - - // Then - val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey.surveyId) - Assert.assertEquals(remoteKey, retrievedRemoteKey) - } - - @Test - fun `insertOrReplace should insert or replace a remote key`() = runTest(testDispatcher) { - // Given - val remoteKey1 = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() - val remoteKey2 = remoteKey1.copy(nextPage = 3) // Updated nextPage - - // When - surveyRemoteKeyDao.insertOrReplace(remoteKey1) - surveyRemoteKeyDao.insertOrReplace(remoteKey2) // Replace existing key - - // Then - val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey1.surveyId) - Assert.assertEquals(remoteKey2, retrievedRemoteKey) // Verify replaced key - } - - @Test - fun `remoteKeysId should return remote key by id`() = runTest(testDispatcher) { - // Given - val remoteKey = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() - surveyRemoteKeyDao.insert(remoteKey) - - // When - val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey.surveyId) - - // Then - Assert.assertEquals(remoteKey, retrievedRemoteKey) - } - - @Test - fun `remoteKeysId should return null when remote key not found`() = runTest(testDispatcher) { - // Given - val nonexistentId = "nonexistent_id" - - // When - val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(nonexistentId) - - // Then - Assert.assertEquals(null, retrievedRemoteKey) - } - - @Test - fun `clearRemoteKeys should clear all remote keys from database`() = runTest(testDispatcher) { - // Given - val remoteKeys = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntityList(count = 2) - surveyRemoteKeyDao.insertAll(remoteKeys) - - // When - surveyRemoteKeyDao.clearRemoteKeys() - - // Then - val allRemoteKeys = surveyRemoteKeyDao.getAll() - Assert.assertEquals(emptyList(), allRemoteKeys) - } -} \ No newline at end of file +//package com.mkdev.data.datasource.local.database.room.dao +// +//import androidx.room.Room +//import androidx.test.core.app.ApplicationProvider +//import androidx.test.ext.junit.runners.AndroidJUnit4 +//import com.mkdev.data.datasource.local.database.room.NimbleRoomDatabase +//import com.mkdev.data.datasource.local.database.room.entity.SurveyRemoteKeyEntity +//import com.mkdev.data.factory.SurveyRemoteKeyEntityFactory +//import kotlinx.coroutines.test.StandardTestDispatcher +//import kotlinx.coroutines.test.runTest +//import org.junit.After +//import org.junit.Assert +//import org.junit.Before +//import org.junit.Test +//import org.junit.runner.RunWith +// +//@RunWith(AndroidJUnit4::class) +//class SurveyRemoteKeyDaoTest { +// +// private lateinit var database: NimbleRoomDatabase +// private lateinit var surveyRemoteKeyDao: SurveyRemoteKeyDao +// private val testDispatcher = StandardTestDispatcher() +// +// @Before +// fun setUp() { +// database = Room.inMemoryDatabaseBuilder( +// ApplicationProvider.getApplicationContext(), +// NimbleRoomDatabase::class.java +// ).allowMainThreadQueries().build() +// surveyRemoteKeyDao = database.surveyRemoteKeyDao() +// } +// +// @After +// fun tearDown() { +// database.close() +// } +// +// @Test +// fun `insertAll should insert remote keys into database`() = runTest(testDispatcher) { +// // Given +// val remoteKeys = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntityList(count = 2) +// +// // When +// surveyRemoteKeyDao.insertAll(remoteKeys) +// +// // Then +// val allRemoteKeys = surveyRemoteKeyDao.getAll() +// Assert.assertEquals(remoteKeys, allRemoteKeys) +// } +// +// @Test +// fun `insert should insert a single remote key into database`() = runTest(testDispatcher) { +// // Given +// val remoteKey = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() +// +// // When +// surveyRemoteKeyDao.insert(remoteKey) +// +// // Then +// val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey.surveyId) +// Assert.assertEquals(remoteKey, retrievedRemoteKey) +// } +// +// @Test +// fun `insertOrReplace should insert or replace a remote key`() = runTest(testDispatcher) { +// // Given +// val remoteKey1 = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() +// val remoteKey2 = remoteKey1.copy(nextPage = 3) // Updated nextPage +// +// // When +// surveyRemoteKeyDao.insertOrReplace(remoteKey1) +// surveyRemoteKeyDao.insertOrReplace(remoteKey2) // Replace existing key +// +// // Then +// val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey1.surveyId) +// Assert.assertEquals(remoteKey2, retrievedRemoteKey) // Verify replaced key +// } +// +// @Test +// fun `remoteKeysId should return remote key by id`() = runTest(testDispatcher) { +// // Given +// val remoteKey = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() +// surveyRemoteKeyDao.insert(remoteKey) +// +// // When +// val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey.surveyId) +// +// // Then +// Assert.assertEquals(remoteKey, retrievedRemoteKey) +// } +// +// @Test +// fun `remoteKeysId should return null when remote key not found`() = runTest(testDispatcher) { +// // Given +// val nonexistentId = "nonexistent_id" +// +// // When +// val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(nonexistentId) +// +// // Then +// Assert.assertEquals(null, retrievedRemoteKey) +// } +// +// @Test +// fun `clearRemoteKeys should clear all remote keys from database`() = runTest(testDispatcher) { +// // Given +// val remoteKeys = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntityList(count = 2) +// surveyRemoteKeyDao.insertAll(remoteKeys) +// +// // When +// surveyRemoteKeyDao.clearRemoteKeys() +// +// // Then +// val allRemoteKeys = surveyRemoteKeyDao.getAll() +// Assert.assertEquals(emptyList(), allRemoteKeys) +// } +//} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff40235..1938739 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ protobufJavalite = "4.29.1" protobuf = "0.9.4" tinkAndroid = "1.7.0" roomRuntime = "2.6.1" -robolectric = "4.11.1" +robolectric = "4.14.1" androidxCoreTesting = "2.2.0" testRules = "1.6.1" kotlinxCoroutinesTest = "1.9.0" diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts index 046a85c..72cbb55 100644 --- a/presentation/build.gradle.kts +++ b/presentation/build.gradle.kts @@ -26,6 +26,13 @@ android { compose = true buildConfig = true } + + flavorDimensions += "environment" + productFlavors { + register("development") + register("staging") + register("production") + } } dependencies { From 842b15d9d8c46ab8c494a7335afca0dda740a1c7 Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 14:57:09 +0330 Subject: [PATCH 09/13] update unit test job --- .github/workflows/android-ci.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index addfca3..d876bef 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -34,14 +34,19 @@ jobs: - name: Checkout the code uses: actions/checkout@v4 + - name: Set capitalized flavor and build type + run: | + echo "CAPITALIZED_FLAVOR=$(echo ${{ matrix.flavor }} | tr '[:lower:]' '[:upper:]')" >> $GITHUB_ENV + echo "CAPITALIZED_BUILD_TYPE=$(echo ${{ matrix.buildType }} | tr '[:lower:]' '[:upper:]')" >> $GITHUB_ENV + - name: Run unit tests for ${{ matrix.flavor }} ${{ matrix.buildType }} - run: ./gradlew test${{ matrix.flavor.capitalize() }}${{ matrix.buildType.capitalize() }}UnitTest + run: ./gradlew test$CAPITALIZED_FLAVOR$CAPITALIZED_BUILD_TYPEUnitTest - name: Upload test report for ${{ matrix.flavor }} ${{ matrix.buildType }} uses: actions/upload-artifact@v4 with: name: unit-test-report-${{ matrix.flavor }}-${{ matrix.buildType }} - path: app/build/reports/tests/test${{ matrix.flavor.capitalize() }}${{ matrix.buildType.capitalize() }}UnitTest/ + path: app/build/reports/tests/test$CAPITALIZED_FLAVOR$CAPITALIZED_BUILD_TYPEUnitTest/ # unit-test: # needs: [lint] From 1675728d2065e42e1d7ea85d2ca77b9faf61489d Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 15:02:30 +0330 Subject: [PATCH 10/13] update unit test job --- .github/workflows/android-ci.yml | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index d876bef..48eb512 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -26,27 +26,12 @@ jobs: unit-test: runs-on: ubuntu-latest - strategy: - matrix: - flavor: [development, staging, production] - buildType: [debug, release] steps: - name: Checkout the code uses: actions/checkout@v4 - - name: Set capitalized flavor and build type - run: | - echo "CAPITALIZED_FLAVOR=$(echo ${{ matrix.flavor }} | tr '[:lower:]' '[:upper:]')" >> $GITHUB_ENV - echo "CAPITALIZED_BUILD_TYPE=$(echo ${{ matrix.buildType }} | tr '[:lower:]' '[:upper:]')" >> $GITHUB_ENV - - - name: Run unit tests for ${{ matrix.flavor }} ${{ matrix.buildType }} - run: ./gradlew test$CAPITALIZED_FLAVOR$CAPITALIZED_BUILD_TYPEUnitTest - - - name: Upload test report for ${{ matrix.flavor }} ${{ matrix.buildType }} - uses: actions/upload-artifact@v4 - with: - name: unit-test-report-${{ matrix.flavor }}-${{ matrix.buildType }} - path: app/build/reports/tests/test$CAPITALIZED_FLAVOR$CAPITALIZED_BUILD_TYPEUnitTest/ + - name: Run tests + run: ./gradlew test # unit-test: # needs: [lint] From d45f15e827168d94ac617c2bb248e35b7977c138 Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 15:13:39 +0330 Subject: [PATCH 11/13] update unit test job --- .github/workflows/android-ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 48eb512..67ffd0f 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -25,6 +25,7 @@ jobs: path: presentation/build/reports/lint-results-debug.html unit-test: +# needs: [lint] runs-on: ubuntu-latest steps: - name: Checkout the code @@ -33,6 +34,12 @@ jobs: - name: Run tests run: ./gradlew test + - name: Upload test report + uses: actions/upload-artifact@v4 + with: + name: unit-test-report + path: app/build/reports/tests/testDevelopmentDebugUnitTest/ + # unit-test: # needs: [lint] # runs-on: ubuntu-latest From bbd8c9aa4c028aeea74089e5aaac8686c5455c13 Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 15:35:30 +0330 Subject: [PATCH 12/13] update unit test job --- .github/workflows/android-ci.yml | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 67ffd0f..bc2bb57 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -25,7 +25,7 @@ jobs: path: presentation/build/reports/lint-results-debug.html unit-test: -# needs: [lint] + needs: [lint] runs-on: ubuntu-latest steps: - name: Checkout the code @@ -40,22 +40,6 @@ jobs: name: unit-test-report path: app/build/reports/tests/testDevelopmentDebugUnitTest/ -# unit-test: -# needs: [lint] -# runs-on: ubuntu-latest -# steps: -# - name: Checkout the code -# uses: actions/checkout@v4 -# -# - name: Run tests -# run: ./gradlew test -# -# - name: Upload test report -# uses: actions/upload-artifact@v4 -# with: -# name: unit_test_report -# path: app/build/reports/tests/testDebugUnitTest/ - # instrumentation-test: # needs: [unit-test] # runs-on: macos-latest @@ -74,4 +58,4 @@ jobs: # uses: actions/upload-artifact@v4 # with: # name: instrumentation_test_report -# path: app/build/reports/androidTests/connected/ \ No newline at end of file +# path: app/build/reports/androidTests/connected/ From 9485d7fa50671b98da8cb0655264ad9419584eec Mon Sep 17 00:00:00 2001 From: Masoud Khoshkam Date: Thu, 26 Dec 2024 18:57:25 +0330 Subject: [PATCH 13/13] fix database test case issue --- app/build.gradle.kts | 2 +- .../authentication/signin/SignInScreenTest.kt | 2 - data/build.gradle.kts | 12 +- .../local/database/room/dao/SurveyDaoTest.kt | 125 +++++++++++++++++ .../room/dao/SurveyRemoteKeyDaoTest.kt | 124 +++++++++++++++++ .../mkdev/data/factory/SurveyEntityFactory.kt | 30 ++++ .../factory/SurveyRemoteKeyEntityFactory.kt | 23 ++++ .../mkdev/data/utils/TestDispatcherRule.kt | 23 ++++ .../local/database/room/dao/SurveyDaoTest.kt | 130 ------------------ .../room/dao/SurveyRemoteKeyDaoTest.kt | 117 ---------------- 10 files changed, 336 insertions(+), 252 deletions(-) create mode 100644 data/src/androidTest/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt create mode 100644 data/src/androidTest/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt create mode 100644 data/src/androidTest/java/com/mkdev/data/factory/SurveyEntityFactory.kt create mode 100644 data/src/androidTest/java/com/mkdev/data/factory/SurveyRemoteKeyEntityFactory.kt create mode 100644 data/src/androidTest/java/com/mkdev/data/utils/TestDispatcherRule.kt delete mode 100644 data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt delete mode 100644 data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index add751b..3970d22 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -117,10 +117,10 @@ dependencies { // Unit Test testImplementation(libs.junit) - testImplementation(libs.mockito) testImplementation(libs.robolectric) testImplementation(libs.androidx.core.testing) testImplementation(libs.kotlinx.coroutines.test) + testImplementation(libs.mockito) testImplementation(libs.mockito.kotlin) testImplementation(libs.mockk) testImplementation(libs.test.rules) diff --git a/app/src/androidTest/java/com/mkdev/nimblesurvey/screen/authentication/signin/SignInScreenTest.kt b/app/src/androidTest/java/com/mkdev/nimblesurvey/screen/authentication/signin/SignInScreenTest.kt index 9a2d346..10e7cf1 100644 --- a/app/src/androidTest/java/com/mkdev/nimblesurvey/screen/authentication/signin/SignInScreenTest.kt +++ b/app/src/androidTest/java/com/mkdev/nimblesurvey/screen/authentication/signin/SignInScreenTest.kt @@ -70,7 +70,6 @@ class SignInScreenTest { composeTestRule.onNodeWithText("Password").performTextInput("12345678") composeTestRule.onNodeWithText("Log in").performClick() - // Wait for navigation to complete (adjust timeout as needed) composeTestRule.waitUntil(timeoutMillis = 2000) { navController.currentDestination?.route == HomeNavigation.ROUTE } @@ -94,7 +93,6 @@ class SignInScreenTest { fun signInScreen_forgotPasswordClick_navigatesToForgotPassword() { composeTestRule.onNodeWithText("Forgot?").performClick() - // Wait for navigation to complete (adjust timeout as needed) composeTestRule.waitUntil(timeoutMillis = 2000) { navController.currentDestination?.route == ResetPasswordNavigation.ROUTE } diff --git a/data/build.gradle.kts b/data/build.gradle.kts index d17851f..5abb81e 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -83,12 +83,20 @@ dependencies { testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockito.kotlin) testImplementation(libs.mockk) - androidTestImplementation(libs.androidx.espresso.core) testImplementation(libs.test.rules) - androidTestImplementation(libs.room.testing) testImplementation(libs.androidx.junit) testImplementation(libs.turbine) testImplementation(libs.truth) + + // UI Test + androidTestImplementation(libs.room.testing) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.hilt.android.testing) + androidTestImplementation(libs.test.runner) + androidTestImplementation(libs.test.rules) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.turbine) + androidTestImplementation(libs.kotlinx.coroutines.test) } protobuf { diff --git a/data/src/androidTest/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt b/data/src/androidTest/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt new file mode 100644 index 0000000..595007c --- /dev/null +++ b/data/src/androidTest/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt @@ -0,0 +1,125 @@ +package com.mkdev.data.datasource.local.database.room.dao + +import android.content.Context +import androidx.paging.PagingSource +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.mkdev.data.datasource.local.database.room.NimbleRoomDatabase +import com.mkdev.data.datasource.local.database.room.entity.SurveyEntity +import com.mkdev.data.factory.SurveyEntityFactory +import com.mkdev.data.utils.TestDispatcherRule +import kotlinx.coroutines.test.runTest +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 + +@RunWith(AndroidJUnit4::class) +class SurveyDaoTest { + + @get: Rule + val dispatcherRule = TestDispatcherRule() + + private lateinit var database: NimbleRoomDatabase + private lateinit var surveyDao: SurveyDao + + @Before + fun setUp() { + val context = ApplicationProvider.getApplicationContext() + database = Room.inMemoryDatabaseBuilder( + context, + NimbleRoomDatabase::class.java + ).build() + surveyDao = database.surveyDao() + } + + @After + fun tearDown() { + database.close() + } + + @Test + fun insertAll_should_insert_surveys_into_database() = runTest { + // Given + val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) + + // When + surveyDao.insertAll(surveys) + + // Then + val insertedSurvey1 = surveyDao.getById(surveys[0].id) + val insertedSurvey2 = surveyDao.getById(surveys[1].id) + + assertEquals(surveys[0], insertedSurvey1) + assertEquals(surveys[1], insertedSurvey2) + } + + @Test + fun getByPaging_should_return_paging_source_of_surveys() = runTest { + // Given + val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) + surveyDao.insertAll(surveys) + + // When + val pagingSource = surveyDao.getByPaging() + val loadResult = pagingSource.load( + PagingSource.LoadParams.Refresh( + key = null, + loadSize = 2, + placeholdersEnabled = false + ) + ) + + // Then + assertEquals(surveys, (loadResult as PagingSource.LoadResult.Page).data) + } + + @Test + fun getById_should_return_survey_by_id() = runTest { + // Given + val survey = SurveyEntityFactory.createSurveyEntity() + surveyDao.insertAll(listOf(survey)) + + // When + val retrievedSurvey = surveyDao.getById(survey.id) + + // Then + assertEquals(survey, retrievedSurvey) + } + + @Test + fun getById_should_return_null_when_survey_not_found() = runTest { + // Given + val nonexistentId = "nonexistent_id" + + // When + val retrievedSurvey = surveyDao.getById(nonexistentId) + + // Then + assertEquals(null, retrievedSurvey) + } + + @Test + fun clearAll_should_clear_all_surveys_from_database() = runTest { + // Given + val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) + surveyDao.insertAll(surveys) + + // When + surveyDao.clearAll() + + // Then + val pagingSource = surveyDao.getByPaging() + val loadResult = pagingSource.load( + PagingSource.LoadParams.Refresh( + key = null, + loadSize = 2, + placeholdersEnabled = false + ) + ) + assertEquals(emptyList(), (loadResult as PagingSource.LoadResult.Page).data) + } +} \ No newline at end of file diff --git a/data/src/androidTest/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt b/data/src/androidTest/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt new file mode 100644 index 0000000..92cd655 --- /dev/null +++ b/data/src/androidTest/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt @@ -0,0 +1,124 @@ +package com.mkdev.data.datasource.local.database.room.dao//package com.mkdev.data.datasource.local.database.room.dao + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.mkdev.data.datasource.local.database.room.NimbleRoomDatabase +import com.mkdev.data.datasource.local.database.room.entity.SurveyRemoteKeyEntity +import com.mkdev.data.factory.SurveyRemoteKeyEntityFactory +import com.mkdev.data.utils.TestDispatcherRule +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SurveyRemoteKeyDaoTest { + + @get: Rule + val dispatcherRule = TestDispatcherRule() + + private lateinit var database: NimbleRoomDatabase + private lateinit var surveyRemoteKeyDao: SurveyRemoteKeyDao + private val testDispatcher = StandardTestDispatcher() + + @Before + fun setUp() { + val context = ApplicationProvider.getApplicationContext() + database = Room.inMemoryDatabaseBuilder( + context, + NimbleRoomDatabase::class.java + ).build() + surveyRemoteKeyDao = database.surveyRemoteKeyDao() + } + + @After + fun tearDown() { + database.close() + } + + @Test + fun insertAll_should_insert_remote_keys_into_database() = runTest { + // Given + val remoteKeys = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntityList(count = 2) + + // When + surveyRemoteKeyDao.insertAll(remoteKeys) + + // Then + val allRemoteKeys = surveyRemoteKeyDao.getAll() + Assert.assertEquals(remoteKeys, allRemoteKeys) + } + + @Test + fun insert_should_insert_a_single_remote_key_into_database() = runTest(testDispatcher) { + // Given + val remoteKey = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() + + // When + surveyRemoteKeyDao.insert(remoteKey) + + // Then + val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey.surveyId) + Assert.assertEquals(remoteKey, retrievedRemoteKey) + } + + @Test + fun insertOrReplace_should_insert_or_replace_a_remote_key() = runTest(testDispatcher) { + // Given + val remoteKey1 = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() + val remoteKey2 = remoteKey1.copy(nextPage = 3) // Updated nextPage + + // When + surveyRemoteKeyDao.insertOrReplace(remoteKey1) + surveyRemoteKeyDao.insertOrReplace(remoteKey2) // Replace existing key + + // Then + val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey1.surveyId) + Assert.assertEquals(remoteKey2, retrievedRemoteKey) // Verify replaced key + } + + @Test + fun remoteKeysId_should_return_remote_key_by_id() = runTest(testDispatcher) { + // Given + val remoteKey = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() + surveyRemoteKeyDao.insert(remoteKey) + + // When + val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey.surveyId) + + // Then + Assert.assertEquals(remoteKey, retrievedRemoteKey) + } + + @Test + fun remoteKeysId_should_return_null_when_remote_key_not_found() = runTest(testDispatcher) { + // Given + val nonexistentId = "nonexistent_id" + + // When + val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(nonexistentId) + + // Then + Assert.assertEquals(null, retrievedRemoteKey) + } + + @Test + fun clearRemoteKeys_should_clear_all_remote_keys_from_database() = runTest(testDispatcher) { + // Given + val remoteKeys = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntityList(count = 2) + surveyRemoteKeyDao.insertAll(remoteKeys) + + // When + surveyRemoteKeyDao.clearRemoteKeys() + + // Then + val allRemoteKeys = surveyRemoteKeyDao.getAll() + Assert.assertEquals(emptyList(), allRemoteKeys) + } +} \ No newline at end of file diff --git a/data/src/androidTest/java/com/mkdev/data/factory/SurveyEntityFactory.kt b/data/src/androidTest/java/com/mkdev/data/factory/SurveyEntityFactory.kt new file mode 100644 index 0000000..5bc0cc8 --- /dev/null +++ b/data/src/androidTest/java/com/mkdev/data/factory/SurveyEntityFactory.kt @@ -0,0 +1,30 @@ +package com.mkdev.data.factory + +import com.mkdev.data.datasource.local.database.room.entity.SurveyEntity + +object SurveyEntityFactory { + + fun createSurveyEntity( + id: String = "survey_id", + title: String = "Survey Title", + description: String = "Survey Description", + coverImageUrl: String = "https://example.com/image.jpg", + isActive: Boolean = true, + surveyType: String = "customer_satisfaction" + ): SurveyEntity { + return SurveyEntity( + id = id, + title = title, + description = description, + coverImageUrl = coverImageUrl, + isActive = isActive, + surveyType = surveyType + ) + } + + fun createSurveyEntityList(count: Int = 5): List { + return (1..count).map { + createSurveyEntity(id = "survey_id_$it") + } + } +} \ No newline at end of file diff --git a/data/src/androidTest/java/com/mkdev/data/factory/SurveyRemoteKeyEntityFactory.kt b/data/src/androidTest/java/com/mkdev/data/factory/SurveyRemoteKeyEntityFactory.kt new file mode 100644 index 0000000..b6b7ae2 --- /dev/null +++ b/data/src/androidTest/java/com/mkdev/data/factory/SurveyRemoteKeyEntityFactory.kt @@ -0,0 +1,23 @@ +package com.mkdev.data.factory + +import com.mkdev.data.datasource.local.database.room.entity.SurveyRemoteKeyEntity + +object SurveyRemoteKeyEntityFactory { + fun createSurveyRemoteKeyEntity( + surveyId: String = "survey_id", + prevPage: Int? = 1, + nextPage: Int? = 2 + ): SurveyRemoteKeyEntity { + return SurveyRemoteKeyEntity( + surveyId = surveyId, + prevPage = prevPage, + nextPage = nextPage + ) + } + + fun createSurveyRemoteKeyEntityList(count: Int = 5): List { + return (1..count).map { + createSurveyRemoteKeyEntity(surveyId = "survey_id_$it") + } + } +} \ No newline at end of file diff --git a/data/src/androidTest/java/com/mkdev/data/utils/TestDispatcherRule.kt b/data/src/androidTest/java/com/mkdev/data/utils/TestDispatcherRule.kt new file mode 100644 index 0000000..d4d26c7 --- /dev/null +++ b/data/src/androidTest/java/com/mkdev/data/utils/TestDispatcherRule.kt @@ -0,0 +1,23 @@ +package com.mkdev.data.utils + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestDispatcher +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.rules.TestWatcher +import org.junit.runner.Description + +@OptIn(ExperimentalCoroutinesApi::class) +class TestDispatcherRule( + private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher() +): TestWatcher() { + override fun starting(description: Description) { + Dispatchers.setMain(testDispatcher) + } + + override fun finished(description: Description) { + Dispatchers.resetMain() + } +} \ No newline at end of file diff --git a/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt b/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt deleted file mode 100644 index 0705767..0000000 --- a/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyDaoTest.kt +++ /dev/null @@ -1,130 +0,0 @@ -//package com.mkdev.data.datasource.local.database.room.dao -// -//import androidx.arch.core.executor.testing.InstantTaskExecutorRule -//import androidx.paging.PagingSource -//import androidx.room.Room -//import androidx.test.core.app.ApplicationProvider -//import androidx.test.ext.junit.runners.AndroidJUnit4 -//import com.mkdev.data.datasource.local.database.room.NimbleRoomDatabase -//import com.mkdev.data.datasource.local.database.room.entity.SurveyEntity -//import com.mkdev.data.factory.SurveyEntityFactory -//import kotlinx.coroutines.test.StandardTestDispatcher -//import kotlinx.coroutines.test.TestDispatcher -//import kotlinx.coroutines.test.runTest -//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.annotation.Config -// -//@RunWith(AndroidJUnit4::class) -//@Config(manifest= Config.NONE) -//class SurveyDaoTest { -// -// @get:Rule -// var instantExecutorRule = InstantTaskExecutorRule() -// -// private lateinit var database: NimbleRoomDatabase -// private lateinit var surveyDao: SurveyDao -// private lateinit var testDispatcher:TestDispatcher -// -// @Before -// fun setUp() { -// database = Room.inMemoryDatabaseBuilder( -// ApplicationProvider.getApplicationContext(), -// NimbleRoomDatabase::class.java -// ).allowMainThreadQueries().build() -// surveyDao = database.surveyDao() -// -// testDispatcher = StandardTestDispatcher() -// } -// -// @After -// fun tearDown() { -// database.close() -// } -// -// @Test -// fun `insertAll should insert surveys into database`() = runTest { -// // Given -// val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) -// -// // When -// surveyDao.insertAll(surveys) -// -// // Then -// val insertedSurvey1 = surveyDao.getById(surveys[0].id) -// val insertedSurvey2 = surveyDao.getById(surveys[1].id) -// -// assertEquals(surveys[0], insertedSurvey1) -// assertEquals(surveys[1], insertedSurvey2) -// } -// -// @Test -// fun `getByPaging should return paging source of surveys`() = runTest(testDispatcher) { -// // Given -// val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) -// surveyDao.insertAll(surveys) -// -// // When -// val pagingSource = surveyDao.getByPaging() -// val loadResult = pagingSource.load( -// PagingSource.LoadParams.Refresh( -// key = null, -// loadSize = 2, -// placeholdersEnabled = false -// ) -// ) -// -// // Then -// assertEquals(surveys, (loadResult as PagingSource.LoadResult.Page).data) -// } -// -// @Test -// fun `getById should return survey by id`() = runTest { -// // Given -// val survey = SurveyEntityFactory.createSurveyEntity() -// surveyDao.insertAll(listOf(survey)) -// -// // When -// val retrievedSurvey = surveyDao.getById(survey.id) -// -// // Then -// assertEquals(survey, retrievedSurvey) -// } -// -// @Test -// fun `getById should return null when survey not found`() = runTest(testDispatcher) { -// // Given -// val nonexistentId = "nonexistent_id" -// -// // When -// val retrievedSurvey = surveyDao.getById(nonexistentId) -// -// // Then -// assertEquals(null, retrievedSurvey) -// } -// -// @Test -// fun `clearAll should clear all surveys from database`() = runTest(testDispatcher) { -// // Given -// val surveys = SurveyEntityFactory.createSurveyEntityList(count = 2) -// surveyDao.insertAll(surveys) -// -// // When -// surveyDao.clearAll() -// -// // Then -// val pagingSource = surveyDao.getByPaging() -// val loadResult = pagingSource.load( -// PagingSource.LoadParams.Refresh( -// key = null, -// loadSize = 2, -// placeholdersEnabled = false -// ) -// ) -// assertEquals(emptyList(), (loadResult as PagingSource.LoadResult.Page).data) -// } -//} \ No newline at end of file diff --git a/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt b/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt deleted file mode 100644 index 3158e32..0000000 --- a/data/src/test/java/com/mkdev/data/datasource/local/database/room/dao/SurveyRemoteKeyDaoTest.kt +++ /dev/null @@ -1,117 +0,0 @@ -//package com.mkdev.data.datasource.local.database.room.dao -// -//import androidx.room.Room -//import androidx.test.core.app.ApplicationProvider -//import androidx.test.ext.junit.runners.AndroidJUnit4 -//import com.mkdev.data.datasource.local.database.room.NimbleRoomDatabase -//import com.mkdev.data.datasource.local.database.room.entity.SurveyRemoteKeyEntity -//import com.mkdev.data.factory.SurveyRemoteKeyEntityFactory -//import kotlinx.coroutines.test.StandardTestDispatcher -//import kotlinx.coroutines.test.runTest -//import org.junit.After -//import org.junit.Assert -//import org.junit.Before -//import org.junit.Test -//import org.junit.runner.RunWith -// -//@RunWith(AndroidJUnit4::class) -//class SurveyRemoteKeyDaoTest { -// -// private lateinit var database: NimbleRoomDatabase -// private lateinit var surveyRemoteKeyDao: SurveyRemoteKeyDao -// private val testDispatcher = StandardTestDispatcher() -// -// @Before -// fun setUp() { -// database = Room.inMemoryDatabaseBuilder( -// ApplicationProvider.getApplicationContext(), -// NimbleRoomDatabase::class.java -// ).allowMainThreadQueries().build() -// surveyRemoteKeyDao = database.surveyRemoteKeyDao() -// } -// -// @After -// fun tearDown() { -// database.close() -// } -// -// @Test -// fun `insertAll should insert remote keys into database`() = runTest(testDispatcher) { -// // Given -// val remoteKeys = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntityList(count = 2) -// -// // When -// surveyRemoteKeyDao.insertAll(remoteKeys) -// -// // Then -// val allRemoteKeys = surveyRemoteKeyDao.getAll() -// Assert.assertEquals(remoteKeys, allRemoteKeys) -// } -// -// @Test -// fun `insert should insert a single remote key into database`() = runTest(testDispatcher) { -// // Given -// val remoteKey = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() -// -// // When -// surveyRemoteKeyDao.insert(remoteKey) -// -// // Then -// val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey.surveyId) -// Assert.assertEquals(remoteKey, retrievedRemoteKey) -// } -// -// @Test -// fun `insertOrReplace should insert or replace a remote key`() = runTest(testDispatcher) { -// // Given -// val remoteKey1 = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() -// val remoteKey2 = remoteKey1.copy(nextPage = 3) // Updated nextPage -// -// // When -// surveyRemoteKeyDao.insertOrReplace(remoteKey1) -// surveyRemoteKeyDao.insertOrReplace(remoteKey2) // Replace existing key -// -// // Then -// val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey1.surveyId) -// Assert.assertEquals(remoteKey2, retrievedRemoteKey) // Verify replaced key -// } -// -// @Test -// fun `remoteKeysId should return remote key by id`() = runTest(testDispatcher) { -// // Given -// val remoteKey = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntity() -// surveyRemoteKeyDao.insert(remoteKey) -// -// // When -// val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(remoteKey.surveyId) -// -// // Then -// Assert.assertEquals(remoteKey, retrievedRemoteKey) -// } -// -// @Test -// fun `remoteKeysId should return null when remote key not found`() = runTest(testDispatcher) { -// // Given -// val nonexistentId = "nonexistent_id" -// -// // When -// val retrievedRemoteKey = surveyRemoteKeyDao.remoteKeysId(nonexistentId) -// -// // Then -// Assert.assertEquals(null, retrievedRemoteKey) -// } -// -// @Test -// fun `clearRemoteKeys should clear all remote keys from database`() = runTest(testDispatcher) { -// // Given -// val remoteKeys = SurveyRemoteKeyEntityFactory.createSurveyRemoteKeyEntityList(count = 2) -// surveyRemoteKeyDao.insertAll(remoteKeys) -// -// // When -// surveyRemoteKeyDao.clearRemoteKeys() -// -// // Then -// val allRemoteKeys = surveyRemoteKeyDao.getAll() -// Assert.assertEquals(emptyList(), allRemoteKeys) -// } -//} \ No newline at end of file