chore(deps): update all libraries and Gradle to latest stable versions
Some checks failed
CI / build (push) Has been cancelled
Some checks failed
CI / build (push) Has been cancelled
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 |
|
| Concern | Choice |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Build | Multi-module Gradle + `:build-logic` **convention plugins**; a single **version catalog** (`gradle/libs.versions.toml`) is the only place versions live |
|
| 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 |
|
| 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 |
|
| Networking | Ktor (OkHttp engine) + KotlinX Serialization |
|
||||||
| Images | Coil 3 |
|
| Images | Coil 3 |
|
||||||
| Navigation | type-safe Compose Navigation (`@Serializable` routes) |
|
| Navigation | type-safe Compose Navigation (`@Serializable` routes) |
|
||||||
| Logging | Timber |
|
| Logging | Timber |
|
||||||
| Async | Coroutines + Flow |
|
| 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
|
> auto-applies the Kotlin Android plugin, so the convention plugins must **not** apply
|
||||||
> `org.jetbrains.kotlin.android` themselves. Source lives in `src/main/kotlin`.
|
> `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
|
## 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.
|
**AssertK**, `kotlinx-coroutines-test`, Ktor **`MockEngine`**, and Compose UI test.
|
||||||
|
|
||||||
| What | Where | Kind |
|
| 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` |
|
| `CharacterListViewModel`, `CharacterDetailViewModel` | `:feature:characters:presentation` `src/test` | JVM unit, MockK + Turbine + `SavedStateHandle` |
|
||||||
| `NetworkCharacterRepository` | `:feature:characters:data` `src/test` | JVM unit, Ktor `MockEngine` |
|
| `NetworkCharacterRepository` | `:feature:characters:data` `src/test` | JVM unit, Ktor `MockEngine` |
|
||||||
| `CharacterListScreen` (robot) | `:feature:characters:presentation-compose` `src/androidTest` | instrumented Compose UI |
|
| `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
|
reads as a scenario; it asserts a rendered item, the empty/error states, and that a tap fires the
|
||||||
right `Action`.
|
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`
|
> 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
|
> convention plugin just calls `useJUnitPlatform()` and adds the `unit-test` bundle - including the
|
||||||
> `junit-platform-launcher`, which Gradle 9 no longer bundles.
|
> `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
|
# Build
|
||||||
./gradlew assembleDebug # build the debug APK
|
./gradlew assembleDebug # build the debug APK
|
||||||
./gradlew projects # print the module tree
|
./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)
|
./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
|
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
|
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).
|
* 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
|
* 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
|
* `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
|
* `useJUnitPlatform()` on it is enough (this mirrors `DomainModuleConventionPlugin`, which does the
|
||||||
* same for pure-JVM modules).
|
* same for pure-JVM modules).
|
||||||
@@ -21,7 +21,7 @@ class AndroidUnitTestConventionPlugin : Plugin<Project> {
|
|||||||
dependencies {
|
dependencies {
|
||||||
add("testImplementation", libs.findBundle("unit-test").get())
|
add("testImplementation", libs.findBundle("unit-test").get())
|
||||||
add("testRuntimeOnly", libs.findLibrary("junit-jupiter-engine").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())
|
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
|
* 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> {
|
class DomainModuleConventionPlugin : Plugin<Project> {
|
||||||
override fun apply(target: Project) = with(target) {
|
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("mockk").get())
|
||||||
add("testImplementation", libs.findLibrary("kotlinx-coroutines-test").get())
|
add("testImplementation", libs.findLibrary("kotlinx-coroutines-test").get())
|
||||||
add("testRuntimeOnly", libs.findLibrary("junit-jupiter-engine").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())
|
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.gradle.kotlin.dsl.getByType
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
|
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 MIN_SDK = 24
|
||||||
internal const val TARGET_SDK = 36
|
internal const val TARGET_SDK = 36
|
||||||
internal const val JVM_TARGET = 17
|
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
|
* 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
|
* 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`.
|
* MockK mock, stubbed with `coEvery` and verified with `coVerify`.
|
||||||
*/
|
*/
|
||||||
class GetCharactersPageUseCaseTest {
|
class GetCharactersPageUseCaseTest {
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
[versions]
|
[versions]
|
||||||
# Build / language
|
# Build / language
|
||||||
agp = "9.0.1"
|
agp = "9.2.1"
|
||||||
kotlin = "2.3.20"
|
kotlin = "2.4.0"
|
||||||
|
|
||||||
# AndroidX - core / lifecycle / activity / views
|
# AndroidX - core / lifecycle / activity / views
|
||||||
androidxCore = "1.18.0"
|
androidxCore = "1.19.0"
|
||||||
androidxLifecycle = "2.10.0"
|
androidxLifecycle = "2.10.0"
|
||||||
androidxActivity = "1.13.0"
|
androidxActivity = "1.13.0"
|
||||||
androidxAppcompat = "1.7.0"
|
androidxAppcompat = "1.7.1"
|
||||||
androidxFragment = "1.8.5"
|
androidxFragment = "1.8.9"
|
||||||
androidxRecyclerview = "1.4.0"
|
androidxRecyclerview = "1.4.0"
|
||||||
androidxNavigation = "2.9.0"
|
androidxNavigation = "2.9.8"
|
||||||
|
|
||||||
# Compose (BOM-managed)
|
# Compose (BOM-managed)
|
||||||
composeBom = "2026.03.01"
|
composeBom = "2026.05.01"
|
||||||
|
|
||||||
# Async / serialization
|
# Async / serialization
|
||||||
coroutines = "1.10.2"
|
coroutines = "1.11.0"
|
||||||
kotlinxSerialization = "1.8.1"
|
kotlinxSerialization = "1.11.0"
|
||||||
kotlinxCollectionsImmutable = "0.3.8"
|
kotlinxCollectionsImmutable = "0.5.0"
|
||||||
|
|
||||||
# DI
|
# DI
|
||||||
koin = "4.1.0"
|
koin = "4.2.1"
|
||||||
|
|
||||||
# Networking
|
# Networking
|
||||||
ktor = "3.1.3"
|
ktor = "3.5.0"
|
||||||
|
|
||||||
# Image loading
|
# Image loading
|
||||||
coil = "3.1.0"
|
coil = "3.5.0"
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
timber = "5.0.1"
|
timber = "5.0.1"
|
||||||
|
|
||||||
# Material Components (Views renderer)
|
# Material Components (Views renderer)
|
||||||
material = "1.12.0"
|
material = "1.14.0"
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
junitJupiter = "5.11.4"
|
junitJupiter = "6.1.0"
|
||||||
junitPlatform = "1.11.4"
|
junitPlatform = "6.1.0"
|
||||||
turbine = "1.2.0"
|
turbine = "1.2.1"
|
||||||
assertk = "0.28.1"
|
assertk = "0.28.1"
|
||||||
mockk = "1.14.3"
|
mockk = "1.14.11"
|
||||||
androidxTestExt = "1.3.0"
|
androidxTestExt = "1.3.0"
|
||||||
androidxTestRunner = "1.7.0"
|
androidxTestRunner = "1.7.0"
|
||||||
androidxEspresso = "3.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
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
Reference in New Issue
Block a user