add Android unit test coverage

This commit is contained in:
2026-04-10 10:03:25 +02:00
parent efe2f9cfea
commit ffed360cb9
5 changed files with 128 additions and 0 deletions
+1
View File
@@ -23,6 +23,7 @@ This is the canonical working plan and progress log for the project. Use this fi
- Backend analytics coverage now includes a representative key user flow batch. - Backend analytics coverage now includes a representative key user flow batch.
- iOS project scaffolding now exists as `mobile/ios-app/HermesApp.xcodeproj` with a shared scheme and XCTest target. - iOS project scaffolding now exists as `mobile/ios-app/HermesApp.xcodeproj` with a shared scheme and XCTest target.
- iOS sources and tests build and pass in Xcode on an iPhone simulator, and a standalone localization/error-mapping harness passes. - iOS sources and tests build and pass in Xcode on an iPhone simulator, and a standalone localization/error-mapping harness passes.
- Android local unit tests now cover localization, error mapping, and analytics batch conversion, and `./gradlew testDebugUnitTest` passes with the Android SDK installed.
### Still Open ### Still Open
+9
View File
@@ -24,6 +24,10 @@ android {
buildConfig = true buildConfig = true
} }
testOptions {
unitTests.isIncludeAndroidResources = true
}
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
@@ -67,4 +71,9 @@ dependencies {
debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest") debugImplementation("androidx.compose.ui:ui-test-manifest")
testImplementation(kotlin("test"))
testImplementation("androidx.test:core:1.6.1")
testImplementation("junit:junit:4.13.2")
testImplementation("org.robolectric:robolectric:4.13")
} }
@@ -0,0 +1,42 @@
package com.hermes.app.core.analytics
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import java.util.UUID
import kotlinx.datetime.Instant
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [34])
class HermesAnalyticsTrackerTest {
@Test
fun convertsPendingEventsToBatchRequest() {
val tracker = HermesAnalyticsTracker()
val event = HermesTrackedEvent(
id = UUID.fromString("00000000-0000-0000-0000-000000000001"),
name = "screen_viewed",
attributes = mapOf("screen_name" to "feed"),
timestamp = Instant.fromEpochMilliseconds(1_000),
)
val batch = tracker.toBatchRequest(listOf(event))
assertEquals(1, batch.events.size)
assertEquals("screen_viewed", batch.events.first().eventName)
assertEquals("feed", batch.events.first().attributes!!.first().value)
}
@Test
fun marksDeliveredEventsAsRemoved() {
val tracker = HermesAnalyticsTracker()
tracker.track("screen_viewed", mapOf("screen_name" to "feed"))
val trackedId = tracker.pendingEventsSnapshot().first().id
tracker.markDelivered(setOf(trackedId))
assertTrue(tracker.pendingEventsSnapshot().isEmpty())
}
}
@@ -0,0 +1,40 @@
package com.hermes.app.core.errors
import androidx.test.core.app.ApplicationProvider
import com.hermes.app.R
import com.hermes.app.core.localization.HermesLocalizationStore
import java.io.IOException
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [34])
class HermesErrorMapperTest {
private val context = ApplicationProvider.getApplicationContext<android.content.Context>()
private val store = HermesLocalizationStore(context)
@Test
fun mapsNetworkErrorsToLocalizedCopy() {
assertEquals(
"Nätverksfel. Kontrollera anslutningen.",
mapUserFacingError(store, "sv", IOException("boom")),
)
}
@Test
fun mapsGenericErrorsToLocalizedCopy() {
assertEquals(
"Försök igen.",
mapUserFacingError(store, "sv", IllegalStateException("boom")),
)
}
@Test
fun ignoresCancellation() {
assertNull(mapUserFacingError(store, "en", kotlinx.coroutines.CancellationException()))
}
}
@@ -0,0 +1,36 @@
package com.hermes.app.core.localization
import androidx.test.core.app.ApplicationProvider
import com.hermes.app.R
import kotlin.test.Test
import kotlin.test.assertEquals
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.junit.runner.RunWith
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [34])
class HermesLocalizationStoreTest {
private val context = ApplicationProvider.getApplicationContext<android.content.Context>()
private val store = HermesLocalizationStore(context)
@Test
fun englishStringsLoad() {
assertEquals("Settings", store.string("en", R.string.settings_title))
assertEquals("Enabled", store.string("en", R.string.settings_enabled))
}
@Test
fun swedishStringsLoad() {
assertEquals("Inställningar", store.string("sv", R.string.settings_title))
assertEquals("Aktiverad", store.string("sv", R.string.settings_enabled))
}
@Test
fun unsupportedLocaleFallsBackToEnglish() {
store.setLocale("fr")
assertEquals("en", store.localeCode.value)
assertEquals("English", store.localeName("en", "fr"))
}
}