chore(deps): update all libraries and Gradle to latest stable versions
Bump the version catalog, Gradle wrapper, and convention plugins to the latest stable releases. Verified with `./gradlew assembleDebug test` (BUILD SUCCESSFUL, 21 unit tests pass). Toolchain: - Gradle 9.1.0 -> 9.5.1 - AGP 9.0.1 -> 9.2.1 - Kotlin 2.3.20 -> 2.4.0 Libraries: - androidx-core 1.18.0 -> 1.19.0, appcompat 1.7.0 -> 1.7.1, fragment 1.8.5 -> 1.8.9, navigation 2.9.0 -> 2.9.8 - compose-bom 2026.03.01 -> 2026.05.01, material 1.12.0 -> 1.14.0 - coroutines 1.10.2 -> 1.11.0, serialization 1.8.1 -> 1.11.0, collections-immutable 0.3.8 -> 0.5.0 - koin 4.1.0 -> 4.2.1, ktor 3.1.3 -> 3.5.0, coil 3.1.0 -> 3.5.0 - JUnit 5.11.4 -> 6.1.0, turbine 1.2.0 -> 1.2.1, mockk 1.14.3 -> 1.14.11 Required side-effect: - compileSdk 36 -> 37 (mandated by androidx.core 1.19.0); targetSdk left at 36. Also refresh stale JUnit 5 / AGP 9.0 / compileSdk 36 references in the README and convention-plugin docs.
This commit is contained in:
18
README.md
18
README.md
@@ -36,17 +36,17 @@ a small MVVM *About* screen for contrast, and a dedicated **error-handling demo*
|
||||
| Concern | Choice |
|
||||
|---|---|
|
||||
| Build | Multi-module Gradle + `:build-logic` **convention plugins**; a single **version catalog** (`gradle/libs.versions.toml`) is the only place versions live |
|
||||
| Toolchain | AGP 9.0.1, Kotlin 2.3.20, Gradle 9.1, `compileSdk`/`targetSdk` 36, `minSdk` 24, Java 17 |
|
||||
| Toolchain | AGP 9.2.1, Kotlin 2.4.0, Gradle 9.5.1, `compileSdk` 37 / `targetSdk` 36, `minSdk` 24, Java 17 |
|
||||
| UI | Jetpack Compose (Material 3) + one classic **Views/XML** renderer |
|
||||
| DI | Koin 4.1 (constructor DSL) |
|
||||
| DI | Koin 4.2 (constructor DSL) |
|
||||
| Networking | Ktor (OkHttp engine) + KotlinX Serialization |
|
||||
| Images | Coil 3 |
|
||||
| Navigation | type-safe Compose Navigation (`@Serializable` routes) |
|
||||
| Logging | Timber |
|
||||
| Async | Coroutines + Flow |
|
||||
| Testing | JUnit 5, MockK, Turbine, AssertK, `kotlinx-coroutines-test`, Ktor `MockEngine`, Compose UI test |
|
||||
| Testing | JUnit 6, MockK, Turbine, AssertK, `kotlinx-coroutines-test`, Ktor `MockEngine`, Compose UI test |
|
||||
|
||||
> **AGP 9 gotcha:** AGP 9.0 has **built-in Kotlin**. Applying `com.android.application`/`library`
|
||||
> **AGP 9 gotcha:** AGP 9.2 has **built-in Kotlin**. Applying `com.android.application`/`library`
|
||||
> auto-applies the Kotlin Android plugin, so the convention plugins must **not** apply
|
||||
> `org.jetbrains.kotlin.android` themselves. Source lives in `src/main/kotlin`.
|
||||
|
||||
@@ -276,12 +276,12 @@ both resolve the **same** `CharacterListViewModel` class and supply its `SavedSt
|
||||
|
||||
## Testing
|
||||
|
||||
Tests prove the architecture, not just the code. Stack: **JUnit 5**, **MockK**, **Turbine** (Flow),
|
||||
Tests prove the architecture, not just the code. Stack: **JUnit 6**, **MockK**, **Turbine** (Flow),
|
||||
**AssertK**, `kotlinx-coroutines-test`, Ktor **`MockEngine`**, and Compose UI test.
|
||||
|
||||
| What | Where | Kind |
|
||||
|---|---|---|
|
||||
| `GetCharactersPageUseCase` | `:feature:characters:domain` `src/test` | pure JVM, JUnit 5 |
|
||||
| `GetCharactersPageUseCase` | `:feature:characters:domain` `src/test` | pure JVM, JUnit 6 |
|
||||
| `CharacterListViewModel`, `CharacterDetailViewModel` | `:feature:characters:presentation` `src/test` | JVM unit, MockK + Turbine + `SavedStateHandle` |
|
||||
| `NetworkCharacterRepository` | `:feature:characters:data` `src/test` | JVM unit, Ktor `MockEngine` |
|
||||
| `CharacterListScreen` (robot) | `:feature:characters:presentation-compose` `src/androidTest` | instrumented Compose UI |
|
||||
@@ -301,7 +301,7 @@ Conventions demonstrated:
|
||||
reads as a scenario; it asserts a rendered item, the empty/error states, and that a tap fires the
|
||||
right `Action`.
|
||||
|
||||
> **JUnit 5 on AGP 9:** the `de.mannodermaus.android-junit5` Gradle plugin targets AGP 8.x, so this
|
||||
> **JUnit 6 on AGP 9:** the `de.mannodermaus.android-junit5` Gradle plugin targets AGP 8.x, so this
|
||||
> repo doesn't use it. `AndroidUnitTest` extends Gradle's `Test`, so the `architecture.android.unit.test`
|
||||
> convention plugin just calls `useJUnitPlatform()` and adds the `unit-test` bundle - including the
|
||||
> `junit-platform-launcher`, which Gradle 9 no longer bundles.
|
||||
@@ -323,7 +323,7 @@ possible but intentionally omitted (the VM logic is already covered by the share
|
||||
# Build
|
||||
./gradlew assembleDebug # build the debug APK
|
||||
./gradlew projects # print the module tree
|
||||
./gradlew test # all JVM unit tests (JUnit 5)
|
||||
./gradlew test # all JVM unit tests (JUnit 6)
|
||||
./gradlew :feature:characters:presentation-compose:connectedDebugAndroidTest # Compose UI test (needs a device)
|
||||
```
|
||||
|
||||
@@ -339,7 +339,7 @@ android docs search "<topic>" # search authoritative Android docs
|
||||
```
|
||||
|
||||
Requires JDK 17+ (the Gradle build pins a Java 17 toolchain) and the Android SDK
|
||||
(`compileSdk 36`, `minSdk 24`).
|
||||
(`compileSdk 37`, `minSdk 24`).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
/**
|
||||
* Runs an Android library module's local unit tests (`src/test`) on the **JUnit 5 platform** with the
|
||||
* Runs an Android library module's local unit tests (`src/test`) on the **JUnit Platform** with the
|
||||
* shared `unit-test` toolset (JUnit Jupiter, kotlinx-coroutines-test, Turbine, AssertK).
|
||||
*
|
||||
* Deliberately does NOT use the `de.mannodermaus.android-junit5` Gradle plugin: its 1.11.x line
|
||||
* targets AGP 8.x and we build on AGP 9.0. It isn't needed for *local* unit tests anyway -
|
||||
* targets AGP 8.x and we build on AGP 9.2. It isn't needed for *local* unit tests anyway -
|
||||
* `com.android.build.gradle.tasks.factory.AndroidUnitTest` extends Gradle's [Test] task, so calling
|
||||
* `useJUnitPlatform()` on it is enough (this mirrors `DomainModuleConventionPlugin`, which does the
|
||||
* same for pure-JVM modules).
|
||||
@@ -21,7 +21,7 @@ class AndroidUnitTestConventionPlugin : Plugin<Project> {
|
||||
dependencies {
|
||||
add("testImplementation", libs.findBundle("unit-test").get())
|
||||
add("testRuntimeOnly", libs.findLibrary("junit-jupiter-engine").get())
|
||||
// Gradle 9 dropped the bundled launcher; JUnit 5 won't start without it.
|
||||
// Gradle 9 dropped the bundled launcher; the JUnit Platform won't start without it.
|
||||
add("testRuntimeOnly", libs.findLibrary("junit-platform-launcher").get())
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.gradle.kotlin.dsl.withType
|
||||
|
||||
/**
|
||||
* Pure-Kotlin (JVM) module for the domain layer: no Android dependencies. Adds Coroutines (for
|
||||
* `Flow`-returning repository interfaces) and runs unit tests on the JUnit 5 platform.
|
||||
* `Flow`-returning repository interfaces) and runs unit tests on the JUnit Platform.
|
||||
*/
|
||||
class DomainModuleConventionPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) = with(target) {
|
||||
@@ -25,7 +25,7 @@ class DomainModuleConventionPlugin : Plugin<Project> {
|
||||
add("testImplementation", libs.findLibrary("mockk").get())
|
||||
add("testImplementation", libs.findLibrary("kotlinx-coroutines-test").get())
|
||||
add("testRuntimeOnly", libs.findLibrary("junit-jupiter-engine").get())
|
||||
// Gradle 9 dropped the bundled launcher; JUnit 5 won't start without it.
|
||||
// Gradle 9 dropped the bundled launcher; the JUnit Platform won't start without it.
|
||||
add("testRuntimeOnly", libs.findLibrary("junit-platform-launcher").get())
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
|
||||
|
||||
internal const val COMPILE_SDK = 36
|
||||
internal const val COMPILE_SDK = 37
|
||||
internal const val MIN_SDK = 24
|
||||
internal const val TARGET_SDK = 36
|
||||
internal const val JVM_TARGET = 17
|
||||
|
||||
@@ -18,7 +18,7 @@ import org.junit.jupiter.api.Test
|
||||
/**
|
||||
* Tests for the (thin pass-through) [GetCharactersPageUseCase]: it must forward the requested page to
|
||||
* the repository and return its result verbatim - success and error alike. Pure JVM test on the
|
||||
* JUnit 5 platform (see DomainModuleConventionPlugin); the [CharacterRepository] collaborator is a
|
||||
* JUnit Platform (see DomainModuleConventionPlugin); the [CharacterRepository] collaborator is a
|
||||
* MockK mock, stubbed with `coEvery` and verified with `coVerify`.
|
||||
*/
|
||||
class GetCharactersPageUseCaseTest {
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
[versions]
|
||||
# Build / language
|
||||
agp = "9.0.1"
|
||||
kotlin = "2.3.20"
|
||||
agp = "9.2.1"
|
||||
kotlin = "2.4.0"
|
||||
|
||||
# AndroidX - core / lifecycle / activity / views
|
||||
androidxCore = "1.18.0"
|
||||
androidxCore = "1.19.0"
|
||||
androidxLifecycle = "2.10.0"
|
||||
androidxActivity = "1.13.0"
|
||||
androidxAppcompat = "1.7.0"
|
||||
androidxFragment = "1.8.5"
|
||||
androidxAppcompat = "1.7.1"
|
||||
androidxFragment = "1.8.9"
|
||||
androidxRecyclerview = "1.4.0"
|
||||
androidxNavigation = "2.9.0"
|
||||
androidxNavigation = "2.9.8"
|
||||
|
||||
# Compose (BOM-managed)
|
||||
composeBom = "2026.03.01"
|
||||
composeBom = "2026.05.01"
|
||||
|
||||
# Async / serialization
|
||||
coroutines = "1.10.2"
|
||||
kotlinxSerialization = "1.8.1"
|
||||
kotlinxCollectionsImmutable = "0.3.8"
|
||||
coroutines = "1.11.0"
|
||||
kotlinxSerialization = "1.11.0"
|
||||
kotlinxCollectionsImmutable = "0.5.0"
|
||||
|
||||
# DI
|
||||
koin = "4.1.0"
|
||||
koin = "4.2.1"
|
||||
|
||||
# Networking
|
||||
ktor = "3.1.3"
|
||||
ktor = "3.5.0"
|
||||
|
||||
# Image loading
|
||||
coil = "3.1.0"
|
||||
coil = "3.5.0"
|
||||
|
||||
# Logging
|
||||
timber = "5.0.1"
|
||||
|
||||
# Material Components (Views renderer)
|
||||
material = "1.12.0"
|
||||
material = "1.14.0"
|
||||
|
||||
# Testing
|
||||
junitJupiter = "5.11.4"
|
||||
junitPlatform = "1.11.4"
|
||||
turbine = "1.2.0"
|
||||
junitJupiter = "6.1.0"
|
||||
junitPlatform = "6.1.0"
|
||||
turbine = "1.2.1"
|
||||
assertk = "0.28.1"
|
||||
mockk = "1.14.3"
|
||||
mockk = "1.14.11"
|
||||
androidxTestExt = "1.3.0"
|
||||
androidxTestRunner = "1.7.0"
|
||||
androidxEspresso = "3.7.0"
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
Reference in New Issue
Block a user