Meshtastic-Android/core/testing
James Rich 2afb82b600 test: migrate MigrationTest to runTest and add missing repository fakes
Part A — runBlocking → runTest
- Migrated all 5 runBlocking sites in core/database MigrationTest
  (@Before + 4 @Test bodies) to kotlinx.coroutines.test.runTest so
  test dispatchers are virtual-time aware and cancellation semantics
  match the rest of the suite.
- Left DatabaseManagerLegacyCleanupTest on runBlocking: it polls with
  delay(100) for async cleanup that runs on Dispatchers.IO. runTest's
  virtual-time delay would collapse instantly and race the real IO
  cleanup, so runBlocking is the correct choice there.

Part B — missing repository fakes
Added five in-memory fakes in core:testing (pure Kotlin, MutableStateFlow
backed) that implement their repository interface exactly:
- FakeDeviceHardwareRepository      (DeviceHardwareRepository)
- FakeFirmwareReleaseRepository     (FirmwareReleaseRepository)
- FakeQuickChatActionRepository     (QuickChatActionRepository)
- FakeTracerouteSnapshotRepository  (TracerouteSnapshotRepository)
- FakeRadioConfigRepository         (RadioConfigRepository)

Each fake is exercised by a new test in
core/testing/src/commonTest/kotlin/.../RepositoryFakesTest.kt:
upsert/delete/reorder flows for quick-chat, seeded lookups +
call-recording for device hardware, stable/alpha emission for
firmware releases, snapshot round-trips for traceroute, and
channel-set mutations for radio config. PacketRepository was
skipped — 40+ members and a FakePacketRepository helper already
exists for informal use; left for a focused follow-up.

Validation (all green):
  ./gradlew spotlessApply spotlessCheck
  ./gradlew :core:testing:jvmTest :core:database:testAndroid
  ./gradlew detekt kmpSmokeCompile

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-17 11:24:15 -05:00
..
src test: migrate MigrationTest to runTest and add missing repository fakes 2026-04-17 11:24:15 -05:00
build.gradle.kts chore: review-cleanup fleet (audit + fix + hardening) (#5158) 2026-04-17 00:02:59 +00:00
README.md Refactor map layer management and navigation infrastructure (#4921) 2026-03-26 00:29:24 +00:00

/*

  • Copyright (c) 2026 Meshtastic LLC
  • This program is free software: you can redistribute it and/or modify
  • it under the terms of the GNU General Public License as published by
  • the Free Software Foundation, either version 3 of the License, or
  • (at your option) any later version.
  • This program is distributed in the hope that it will be useful,
  • but WITHOUT ANY WARRANTY; without even the implied warranty of
  • MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  • GNU General Public License for more details.
  • You should have received a copy of the GNU General Public License
  • along with this program. If not, see https://www.gnu.org/licenses/. */

:core:testing

Module dependency graph

graph TB
  :core:testing[testing]:::kmp-library

classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef compose-desktop-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef android-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000;
classDef android-library fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000;
classDef android-library-compose fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000;
classDef android-test fill:#A0C4FF,stroke:#000,stroke-width:2px,color:#000;
classDef jvm-library fill:#BDB2FF,stroke:#000,stroke-width:2px,color:#000;
classDef kmp-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000;
classDef kmp-library-compose fill:#FFC1CC,stroke:#000,stroke-width:2px,color:#000;
classDef kmp-library fill:#FFC1CC,stroke:#000,stroke-width:2px,color:#000;
classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000;

Overview

The :core:testing module is a dedicated Kotlin Multiplatform (KMP) library that provides shared test fakes, doubles, rules, and utilities. It is designed to be consumed by the commonTest source sets of all other KMP modules to ensure consistent and unified testing behavior across the codebase.

By centralizing fakes and mocking utilities here, we prevent duplication of test setups and enforce a standard approach to testing ViewModels, Repositories, and pure domain logic.

Handling Platform-Specific Setup (Robolectric)

Some KMP modules interact with Android framework components (e.g., android.net.Uri, androidx.room, DataStore) that require Robolectric to run on the JVM. To maintain a unified test suite while providing platform-specific initialization, follow the Subclassing Pattern:

1. Create an Abstract Base Test in commonTest

Place your test logic in an abstract class in src/commonTest. Do NOT use @BeforeTest for setup that requires platform-specific context.

abstract class CommonMyViewModelTest {
    protected lateinit var viewModel: MyViewModel
    
    // Call this from subclasses
    fun setupRepo() {
        // ... common setup logic
    }
    
    @Test
    fun testLogic() { /* ... */ }
}

2. Implement the JVM Subclass in jvmTest

A simple subclass is usually enough for pure JVM targets.

class MyViewModelTest : CommonMyViewModelTest() {
    @BeforeTest
    fun setup() {
        setupRepo()
    }
}

3. Implement the Android Subclass in androidHostTest

Use @RunWith(RobolectricTestRunner::class) and call setupTestContext() to initialize ContextServices.app.

@RunWith(RobolectricTestRunner::class)
@Config(sdk = [34])
class MyViewModelTest : CommonMyViewModelTest() {
    @BeforeTest
    fun setup() {
        setupTestContext() // From :core:testing, initializes Robolectric context
        setupRepo()
    }
}

Key Components

  • Test Doubles / Fakes: Provides in-memory implementations of core repositories (e.g., FakeNodeRepository, FakeMeshLogRepository) to isolate components under test.
  • Coroutines Testing: Provides dispatchers and test rules that replace the main dispatcher with TestDispatcher to allow time-control and synchronous execution of coroutines in tests.
  • Mokkery Support: Integrated with the Mokkery compiler plugin to provide robust and unified mocking capabilities in commonTest.

Usage

Add this module to your commonTest source set dependencies in your KMP module's build.gradle.kts:

kotlin {
    sourceSets {
        commonTest.dependencies {
            implementation(projects.core.testing)
        }
    }
}