android-native-dev について
android-native-devは、KotlinとJetpack Composeを使ったAndroidアプリ開発向けのスキルです。プロジェクト設定、コーディング規約、UIの実践方法、Gradleのビルドエラー診断を開発者に案内します。
主な機能
- 01Androidプロジェクトの初期化とGradle設定
- 02Kotlinのコーディングとコルーチンの指針
- 03Jetpack Composeの状態管理とUI設計
- 04ビルドバリアントとフレーバーの設定
- 05ビルドエラーの診断とデバッグ
活用シーン
- 01空のディレクトリからAndroidプロジェクトを開始
- 02開発・ステージング・本番用のフレーバー追加
- 03KotlinとComposeコードのよくある問題の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ minimax-ai/ skills --skill android-native-devインストール後、Claude Code で/android-native-devを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/accessibility.mdadaptive-screens.mddesign-style-guide.mdfunctional-requirements.mdmotion-system.mdperformance-stability.mdprivacy-security.mdtesting.mdvisual-design.md詳細
1. プロジェクトのシナリオ評価
開発を始める前に、現在のプロジェクトの状態を評価します。
| シナリオ | 特徴 | 対応方法 |
|---|---|---|
| 空のディレクトリ | ファイルが存在しない | Gradle Wrapper を含め、全体を初期化する必要があります |
| Gradle Wrapper がある | gradlew と gradle/wrapper/ が存在する | ビルドには ./gradlew を直接使用します |
| Android Studio プロジェクト | プロジェクト構成は完全ですが、Wrapper がない場合があります | Wrapper を確認し、必要に応じて gradle wrapper を実行します |
| 不完全なプロジェクト | 一部のファイルが存在する | 不足しているファイルを確認し、設定を補完します |
基本原則:
- ビジネスロジックを記述する前に、
./gradlew assembleDebugが成功することを確認します gradle.propertiesがない場合は、まず作成して AndroidX を設定します
1.1 必須ファイルのチェックリスト
MyApp/
├── gradle.properties # Configure AndroidX and other settings
├── settings.gradle.kts
├── build.gradle.kts # Root level
├── gradle/wrapper/
│ └── gradle-wrapper.properties
├── app/
│ ├── build.gradle.kts # Module level
│ └── src/main/
│ ├── AndroidManifest.xml
│ ├── java/com/example/myapp/
│ │ └── MainActivity.kt
│ └── res/
│ ├── values/
│ │ ├── strings.xml
│ │ ├── colors.xml
│ │ └── themes.xml
│ └── mipmap-*/ # App icons
2. プロジェクトの設定
2.1 gradle.properties
# Required configuration
android.useAndroidX=true
android.enableJetifier=true
# Build optimization
org.gradle.parallel=true
kotlin.code.style=official
# JVM memory settings (adjust based on project size)
# Small projects: 2048m, Medium: 4096m, Large: 8192m+
# org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
注:ビルド中に
OutOfMemoryErrorが発生した場合は、-Xmxの値を増やしてください。依存関係が多い大規模なプロジェクトでは、8GB 以上が必要になる場合があります。
2.2 依存関係の宣言に関する規則
dependencies {
// Use BOM to manage Compose versions
implementation(platform("androidx.compose:compose-bom:2024.02.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
// Activity & ViewModel
implementation("androidx.activity:activity-compose:1.8.2")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
}
2.3 ビルドバリアントとプロダクトフレーバー
プロダクトフレーバーを使うと、アプリの異なるバージョン(無料版/有料版、開発版/ステージング版/本番版など)を作成できます。
app/build.gradle.kts での設定:
android {
// Define flavor dimensions
flavorDimensions += "environment"
productFlavors {
create("dev") {
dimension = "environment"
applicationIdSuffix = ".dev"
versionNameSuffix = "-dev"
// Different config values per flavor
buildConfigField("String", "API_BASE_URL", "\"https://dev-api.example.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "true")
// Different resources
resValue("string", "app_name", "MyApp Dev")
}
create("staging") {
dimension = "environment"
applicationIdSuffix = ".staging"
versionNameSuffix = "-staging"
buildConfigField("String", "API_BASE_URL", "\"https://staging-api.example.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "true")
resValue("string", "app_name", "MyApp Staging")
}
create("prod") {
dimension = "environment"
// No suffix for production
buildConfigField("String", "API_BASE_URL", "\"https://api.example.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "false")
resValue("string", "app_name", "MyApp")
}
}
buildTypes {
debug {
isDebuggable = true
isMinifyEnabled = false
}
release {
isDebuggable = false
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
}
ビルドバリアントの命名:{flavor}{BuildType} → 例:devDebug、prodRelease
Gradle のビルドコマンド:
# List all available build variants
./gradlew tasks --group="build"
# Build specific variant (flavor + buildType)
./gradlew assembleDevDebug # Dev flavor, Debug build
./gradlew assembleStagingDebug # Staging flavor, Debug build
./gradlew assembleProdRelease # Prod flavor, Release build
# Build all variants of a specific flavor
./gradlew assembleDev # All Dev variants (debug + release)
./gradlew assembleProd # All Prod variants
# Build all variants of a specific build type
./gradlew assembleDebug # All flavors, Debug build
./gradlew assembleRelease # All flavors, Release build
# Install specific variant to device
./gradlew installDevDebug
./gradlew installProdRelease
# Build and install in one command
./gradlew installDevDebug && adb shell am start -n com.example.myapp.dev/.MainActivity
コードからの BuildConfig へのアクセス:
注:AGP 8.0 以降では、
BuildConfigはデフォルトで生成されなくなりました。build.gradle.ktsで明示的に有効にする必要があります。android { buildFeatures { buildConfig = true } }
// Use build config values in your code
val apiUrl = BuildConfig.API_BASE_URL
val isLoggingEnabled = BuildConfig.ENABLE_LOGGING
if (BuildConfig.DEBUG) {
// Debug-only code
}
フレーバー固有のソースセット:
app/src/
├── main/ # Shared code for all flavors
├── dev/ # Dev-only code and resources
│ ├── java/
│ └── res/
├── staging/ # Staging-only code and resources
├── prod/ # Prod-only code and resources
├── debug/ # Debug build type code
└── release/ # Release build type code
複数のフレーバーディメンション(例:環境 + プラン):
android {
flavorDimensions += listOf("environment", "tier")
productFlavors {
create("dev") { dimension = "environment" }
create("prod") { dimension = "environment" }
create("free") { dimension = "tier" }
create("paid") { dimension = "tier" }
}
}
// Results in: devFreeDebug, devPaidDebug, prodFreeRelease, etc.
3. Kotlin 開発規則
3.1 命名規則
| 種類 | 規則 | 例 |
|---|---|---|
| クラス/インターフェース | PascalCase | UserRepository、MainActivity |
| 関数/変数 | camelCase | getUserName()、isLoading |
| 定数 | SCREAMING_SNAKE | MAX_RETRY_COUNT |
| パッケージ | 小文字 | com.example.myapp |
| Composable | PascalCase | @Composable fun UserCard() |
3.2 コード規則(重要)
Null 安全性:
// ❌ Avoid: Non-null assertion !! (may crash)
val name = user!!.name
// ✅ Recommended: Safe call + default value
val name = user?.name ?: "Unknown"
// ✅ Recommended: let handling
user?.let { processUser(it) }
例外処理:
// ❌ Avoid: Random try-catch in business layer swallowing exceptions
fun loadData() {
try {
val data = api.fetch()
} catch (e: Exception) {
// Swallowing exception, hard to debug
}
}
// ✅ Recommended: Let exceptions propagate, handle at appropriate layer
suspend fun loadData(): Result<Data> {
return try {
Result.success(api.fetch())
} catch (e: Exception) {
Result.failure(e) // Wrap and return, let caller decide handling
}
}
// ✅ Recommended: Unified handling in ViewModel
viewModelScope.launch {
runCatching { repository.loadData() }
.onSuccess { _uiState.value = UiState.Success(it) }
.onFailure { _uiState.value = UiState.Error(it.message) }
}
3.3 スレッドとコルーチン(重要)
スレッド選択の原則:
| 処理の種類 | スレッド | 説明 |
|---|---|---|
| UI の更新 | Dispatchers.Main | View、State、LiveData を更新します |
| ネットワークリクエスト | Dispatchers.IO | HTTP 呼び出し、API リクエスト |
| ファイル I/O | Dispatchers.IO | ローカルストレージ、データベース操作 |
| 計算負荷の高い処理 | Dispatchers.Default | JSON の解析、ソート、暗号化 |
正しい使用方法:
// In ViewModel
viewModelScope.launch {
// Default Main thread, can update UI State
_uiState.value = UiState.Loading
// Switch to IO thread for network request
val result = withContext(Dispatchers.IO) {
repository.fetchData()
}
// Automatically returns to Main thread, update UI
_uiState.value = UiState.Success(result)
}
// In Repository (suspend functions should be main-safe)
suspend fun fetchData(): Data = withContext(Dispatchers.IO) {
api.getData()
}
よくある間違い:
// ❌ Wrong: Updating UI on IO thread
viewModelScope.launch(Dispatchers.IO) {
val data = api.fetch()
_uiState.value = data // Crash or warning!
}
// ❌ Wrong: Executing time-consuming operation on Main thread
viewModelScope.launch {
val data = api.fetch() // Blocking main thread! ANR
}
// ✅ Correct: Fetch on IO, update on Main
viewModelScope.launch {
val data = withContext(Dispatchers.IO) { api.fetch() }
_uiState.value = data
}
3.4 可視性の規則
// Default is public, declare explicitly when needed
class UserRepository { // public
private val cache = mutableMapOf<String, User>() // Visible only within class
internal fun clearCache() {} // Visible only within module
}
// data class properties are public by default, be careful when used across modules
data class User(
val id: String, // public
val name: String
)
3.5 よくある構文上の落とし穴
// ❌ Wrong: Accessing uninitialized lateinit
class MyViewModel : ViewModel() {
lateinit var data: String
fun process() = data.length // May crash
}
// ✅ Correct: Use nullable or default value
class MyViewModel : ViewModel() {
var data: String? = null
fun process() = data?.length ?: 0
}
// ❌ Wrong: Using return in lambda
list.forEach { item ->
if (item.isEmpty()) return // Returns from outer function!
}
// ✅ Correct: Use return@forEach
list.forEach { item ->
if (item.isEmpty()) return@forEach
}
3.6 サーバー応答のデータクラスのフィールドは Nullable にする必要があります
// ❌ Wrong: Fields declared as non-null (server may not return them)
data class UserResponse(
val id: String = "",
val name: String = "",
val avatar: String = ""
)
// ✅ Correct: All fields declared as nullable
data class UserResponse(
@SerializedName("id")
val id: String? = null,
@SerializedName("name")
val name: String? = null,
@SerializedName("avatar")
val avatar: String? = null
)
3.7 ライフサイクルに応じたリソース管理
// ❌ Wrong: Only adding Observer, not removing
class MyView : View {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
activity?.lifecycle?.addObserver(this)
}
// Memory leak!
}
// ✅ Correct: Paired add and remove
class MyView : View {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
activity?.lifecycle?.addObserver(this)
}
override fun onDetachedFromWindow() {
activity?.lifecycle?.removeObserver(this)
super.onDetachedFromWindow()
}
}
3.8 ログレベルの使い分け
import android.util.Log
// Info: Key checkpoints in normal flow
Log.i(TAG, "loadData: started, userId = $userId")
// Warning: Abnormal but recoverable situations
Log.w(TAG, "loadData: cache miss, fallback to network")
// Error: Failure/error situations
Log.e(TAG, "loadData failed: ${error.message}")
| レベル | 用途 |
|---|---|
i(情報) | 通常の処理の流れ、メソッドの開始、重要なパラメーター |
w(警告) | 回復可能な例外、フォールバック処理、null の返却 |
e(エラー) | リクエストの失敗、捕捉した例外、回復不能なエラー |
4. Jetpack Compose の規則
4.1 @Composable コンテキストの規則
// ❌ Wrong: Calling Composable from non-Composable function
fun showError(message: String) {
Text(message) // Compile error!
}
// ✅ Correct: Mark as @Composable
@Composable
fun ErrorMessage(message: String) {
Text(message)
}
// ❌ Wrong: Using suspend outside LaunchedEffect
@Composable
fun MyScreen() {
val data = fetchData() // Error!
}
// ✅ Correct: Use LaunchedEffect
@Composable
fun MyScreen() {
var data by remember { mutableStateOf<Data?>(null) }
LaunchedEffect(Unit) {
data = fetchData()
}
}
4.2 状態管理
// Basic State
var count by remember { mutableStateOf(0) }
// Derived State (avoid redundant computation)
val isEven by remember { derivedStateOf { count % 2 == 0 } }
// Persist across recomposition (e.g., scroll position)
val scrollState = rememberScrollState()
// State in ViewModel
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
}
4.3 Compose でよくある間違い
// ❌ Wrong: Creating objects in Composable (created on every recomposition)
@Composable
fun MyScreen() {
val viewModel = MyViewModel() // Wrong!
}
// ✅ Correct: Use viewModel() or remember
@Composable
fun MyScreen(viewModel: MyViewModel = viewModel()) {
// ...
}
5. リソースとアイコン
5.1 アプリアイコンの要件
複数の解像度のアイコンを用意する必要があります。
| ディレクトリ | サイズ | 用途 |
|---|---|---|
| mipmap-mdpi | 48x48 | 基準 |
| mipmap-hdpi | 72x72 | 1.5 倍 |
| mipmap-xhdpi | 96x96 | 2 倍 |
| mipmap-xxhdpi | 144x144 | 3 倍 |
| mipmap-xxxhdpi | 192x192 | 4 倍 |
推奨:アダプティブアイコンを使用します(Android 8 以降)。
<adaptive-icon>
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
5.2 リソースの命名規則
| 種類 | 接頭辞 | 例 |
|---|---|---|
| レイアウト | layout_ | layout_main.xml |
| 画像 | ic_、img_、bg_ | ic_user.png |
| 色 | color_ | color_primary |
| 文字列 | - | app_name、btn_submit |
5.3 Android の予約済み名称を避ける(重要)
変数名、リソース ID、色、アイコン、XML 要素には、Android の予約語やシステムリソース名を使用しないでください。予約済みの名前を使用すると、ビルドエラーやリソースの競合が発生します。
避けるべき一般的な予約済み名称:
| カテゴリ | 予約済み名称(使用禁止) |
|---|---|
| 色 | background、foreground、transparent、white、black |
| アイコン/ドローアブル | icon、logo、image、drawable |
| ビュー | view、text、button、layout、container |
| 属性 | id、name、type、style、theme、color |
| システム | app、android、content、data、action |
例:
<color name="background">#FFFFFF</color>
<color name="icon">#000000</color>
<color name="app_background">#FFFFFF</color>
<color name="icon_primary">#000000</color>
// ❌ Wrong: Variable names conflict with system
val icon = R.drawable.my_icon
val background = Color.White
// ✅ Correct: Use descriptive names
val appIcon = R.drawable.my_icon
val screenBackground = Color.White
<ImageView android:src="@drawable/icon" />
<ImageView android:src="@drawable/ic_home" />
6. ビルドエラーの診断と修正
6.1 よくあるエラーのクイックリファレンス
| エラーキーワード | 原因 | 修正方法 |
|---|---|---|
Unresolved reference | インポートの不足、または未定義 | インポートを確認し、依存関係を検証します |
Type mismatch | 型の不一致 | パラメーターの型を確認し、変換を追加します |
Cannot access | 可視性の問題 | public/private/internal を確認します |
@Composable invocations | Composable コンテキストのエラー | 呼び出し元も @Composable であることを確認します |
Duplicate class | 依存関係の競合 | ./gradlew dependencies を使用して調査します |
AAPT: error | リソースファイルのエラー | XML 構文とリソース参照を確認します |
6.2 修正のベストプラクティス
- まずエラーメッセージ全体を読む:ファイルと行番号を特定します
- 最近の変更を確認する:問題は通常、直近の変更にあります
- ビルドをクリーンにする:
./gradlew clean assembleDebug - 依存関係のバージョンを確認する:バージョンの競合はよくある原因です
- 必要に応じて依存関係を更新する:キャッシュをクリアして再ビルドします
6.3 デバッグコマンド
# Clean and build
./gradlew clean assembleDebug
# View dependency tree (investigate conflicts)
./gradlew :app:dependencies
# View detailed errors
./gradlew assembleDebug --stacktrace
# Refresh dependencies
./gradlew --refresh-dependencies
7. Material Design 3 のガイドライン
Android の UI ファイルを確認し、Material Design 3 のガイドラインと Android のベストプラクティスに準拠していることを確かめます。
デザインの理念
M3 の基本原則
| 原則 | 説明 |
|---|---|
| パーソナル | ユーザーの好みや壁紙に基づくダイナミックカラー |
| 適応性 | すべての画面サイズとフォームファクターに対応 |
| 表現力 | 個性のある大胆な色とタイポグラフィ |
| アクセシブル | すべてのユーザーを対象としたインクルーシブなデザイン |
M3 Expressive(最新)
最新の進化では、感情を重視した UX を次の要素によって実現します:
- 鮮やかなダイナミックカラー
- 直感的な動きの物理特性
- 適応型コンポーネント
- 柔軟なタイポグラフィ
- コントラストのある形状(新たに 35 種類の形状オプション)
アプリのスタイル選択
重要な判断:視覚スタイルをアプリのカテゴリと対象ユーザーに合わせます。
| アプリのカテゴリ | 視覚スタイル | 主な特徴 |
|---|---|---|
| ユーティリティ/ツール | ミニマリスト | すっきりとして効率的、中立的な色 |
| 金融/銀行 | 信頼感のあるプロフェッショナル | 控えめな色、安全性を重視 |
| 健康/ウェルネス | 穏やかで自然 | 柔らかな色、有機的な形状 |
| 子ども向け(3~5 歳) | シンプルで遊び心のあるデザイン | 明るい色、大きなタッチ領域(56dp 以上) |
| 子ども向け(6~12 歳) | 楽しく夢中になれるデザイン | 鮮やかで、ゲーム感覚のフィードバック |
| ソーシャル/エンターテインメント | 表現力豊か | ブランド主導で、ジェスチャーを多用 |
| 生産性 | すっきりとして集中しやすい | ミニマルで、高いコントラスト |
| E コマース | コンバージョン重視 | 明確な CTA、スキャンしやすいレイアウト |
詳しいスタイルプロファイルについては、デザインスタイルガイドを参照してください。
クイックリファレンス:主な仕様
色のコントラスト要件
| 要素 | 最小比率 |
|---|---|
| 本文 | 4.5:1 |
| 大きなテキスト(18sp 以上) | 3:1 |
| UI コンポーネント | 3:1 |
タッチターゲット
| 種類 | サイズ |
|---|---|
| 最小 | 48 × 48dp |
| 推奨(主要な操作) | 56 × 56dp |
| 子ども向けアプリ | 56dp 以上 |
| ターゲット間の間隔 | 最小 8dp |
8dp グリッドシステム
| トークン | 値 | 用途 |
|---|---|---|
| xs | 4dp | アイコンのパディング |
| sm | 8dp | 狭い間隔 |
| md | 16dp | デフォルトのパディング |
| lg | 24dp | セクション間の間隔 |
| xl | 32dp | 大きな間隔 |
| xxl | 48dp | 画面の余白 |
タイポグラフィのスケール(概要)
| カテゴリ | サイズ |
|---|---|
| ディスプレイ | 57sp、45sp、36sp |
| 見出し | 32sp、28sp、24sp |
| タイトル | 22sp、16sp、14sp |
| 本文 | 16sp、14sp、12sp |
| ラベル | 14sp、12sp、11sp |
アニメーションの時間
| 種類 | 時間 |
|---|---|
| マイクロ(波紋) | 50-100ms |
| 短い(シンプル) | 100-200ms |
| 中程度(展開/折りたたみ) | 200-300ms |
| 長い(複雑) | 300-500ms |
コンポーネントの寸法
| コンポーネント | 高さ | 最小幅 |
|---|---|---|
| ボタン | 40dp | 64dp |
| FAB | 56dp | 56dp |
| テキストフィールド | 56dp | 280dp |
| アプリバー | 64dp | - |
| ボトムナビゲーション | 80dp | - |
アンチパターン(必ず避けること)
UI のアンチパターン
- ボトムナビゲーションの項目が 5 つを超える
- 同じ画面に複数の FAB を配置する
- タッチターゲットが 48dp 未満
- 間隔に一貫性がない(8dp の倍数ではない)
- ダークテーマに対応していない
- コントラストを確認せずに色付きの背景にテキストを配置する
パフォーマンスのアンチパターン
- 進行状況インジケーターがない状態で起動時間が 2 秒を超える
- フレームレートが 60 FPS 未満(1 フレームあたり 16ms 超)
- クラッシュ率が 1.09% を超える(Google Play のしきい値)
- ANR 率が 0.47% を超える(Google Play のしきい値)
アクセシビリティのアンチパターン
- インタラクティブ要素に contentDescription が設定されていない
- ラベルに要素の種類が含まれている(例:「保存ボタン」ではなく「保存」)
- 子ども向けアプリで複雑なジェスチャーを使う
- 読み書きができないユーザー向けにテキストのみのボタンを使う
レビューチェックリスト
- 8dp 間隔グリッドに準拠している
- タッチターゲットが最小 48dp である
- タイポグラフィのスケールが適切に使われている
- 色のコントラストに準拠している(テキストは 4.5:1 以上)
- ダークテーマに対応している
- すべてのインタラクティブ要素に contentDescription が設定されている
- 起動時間が 2 秒未満、または進行状況を表示する
- ビジュアルスタイルがアプリのカテゴリに合っている
デザインの参考資料
| トピック | 参考資料 |
|---|---|
| 色、タイポグラフィ、間隔、形状 | ビジュアルデザイン |
| アニメーションとトランジション | モーションシステム |
| アクセシビリティのガイドライン | アクセシビリティ |
| 大画面と折りたたみ式デバイス | アダプティブ画面 |
| Android Vitals とパフォーマンス | パフォーマンスと安定性 |
| プライバシーとセキュリティ | プライバシーとセキュリティ |
| オーディオ、動画、通知 | 機能要件 |
| カテゴリ別のアプリスタイル | デザインスタイルガイド |
8. テスト
注:ユーザーがテストを明示的に求めた場合にのみ、テストの依存関係を追加してください。
十分にテストされた Android アプリでは、階層化されたテストを使用します。ロジックには高速なローカル単体テスト、UI と統合にはインストルメンテーションテストを使い、Gradle Managed Devices でエミュレーターを再現性高く実行します。CI を含め、どのマシンでも実行できます。
8.1 テスト依存関係
テスト依存関係を追加する前に、競合を避けるため、プロジェクトで既に使用されているバージョンを確認します。
gradle/libs.versions.tomlを確認します。存在する場合は、プロジェクトのバージョンカタログのスタイルに合わせてテスト依存関係を追加します- 既存の
build.gradle.ktsを確認し、バージョンが既に固定された依存関係がないか確認します - 以下の表に従って、バージョン系列を合わせます
バージョンの整合性ルール:
| テスト依存関係 | 整合させる対象 | 確認方法 |
|---|---|---|
kotlinx-coroutines-test | プロジェクトの kotlinx-coroutines-core バージョン | ビルドファイルまたはバージョンカタログで kotlinx-coroutines を検索します |
compose-ui-test-junit4 | プロジェクトの Compose BOM または compose-compiler | ビルドファイルで compose-bom または compose.compiler を検索します |
espresso-* | すべての Espresso アーティファクトで同じバージョンを使用します | ビルドファイルで espresso を検索します |
androidx.test:runner、rules、ext:junit | 互換性のある AndroidX Test バージョンを使用します | ビルドファイルで androidx.test を検索します |
mockk | プロジェクトの Kotlin バージョンをサポートしている必要があります | ルートの build.gradle.kts またはバージョンカタログで kotlin のバージョンを確認します |
依存関係のリファレンス — 必要なグループだけを追加します。
dependencies {
// --- Local unit tests (src/test/) ---
testImplementation("junit:junit:<version>") // 4.13.2+
testImplementation("org.robolectric:robolectric:<version>") // 4.16.1+
testImplementation("io.mockk:mockk:<version>") // match Kotlin version
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:<version>") // match coroutines-core
testImplementation("androidx.arch.core:core-testing:<version>") // InstantTaskExecutorRule for LiveData
testImplementation("app.cash.turbine:turbine:<version>") // Flow/StateFlow testing
// --- Instrumentation tests (src/androidTest/) ---
androidTestImplementation("androidx.test.ext:junit:<version>")
androidTestImplementation("androidx.test:runner:<version>")
androidTestImplementation("androidx.test:rules:<version>")
androidTestImplementation("androidx.test.espresso:espresso-core:<version>")
androidTestImplementation("androidx.test.espresso:espresso-contrib:<version>") // RecyclerView, Drawer
androidTestImplementation("androidx.test.espresso:espresso-intents:<version>") // Intent verification
androidTestImplementation("androidx.test.espresso:espresso-idling-resource:<version>")
androidTestImplementation("androidx.test.uiautomator:uiautomator:<version>")
// --- Compose UI tests (only if project uses Compose) ---
androidTestImplementation("androidx.compose.ui:ui-test-junit4") // version from Compose BOM
debugImplementation("androidx.compose.ui:ui-test-manifest") // required for createComposeRule
}
注:プロジェクトで Compose BOM を使用している場合、
ui-test-junit4とui-test-manifestに明示的なバージョンを指定する必要はありません。BOM が管理します。
android ブロックで Robolectric のリソースサポートを有効にします。
android {
testOptions {
unitTests.isIncludeAndroidResources = true // required for Robolectric
}
}
8.2 レイヤーごとのテスト
| レイヤー | 場所 | 実行環境 | 速度 | 用途 |
|---|---|---|---|---|
| ユニット(JUnit) | src/test/ | JVM | 約 ms | ViewModel、リポジトリ、マッパー、バリデーター |
| ユニット+Robolectric | src/test/ | JVM+シミュレートされた Android | 約100ms | Context、リソース、SharedPrefs が必要なコード |
| Compose UI(ローカル) | src/test/ | JVM+Robolectric | 約100ms | Composable のレンダリングと操作 |
| Espresso | src/androidTest/ | デバイス/エミュレーター | 約数秒 | View ベースの UI フロー、Intent、DB 統合 |
| Compose UI(デバイス) | src/androidTest/ | デバイス/エミュレーター | 約数秒 | 実際のレンダリングを使った Compose UI の完全なフロー |
| UI Automator | src/androidTest/ | デバイス/エミュレーター | 約数秒 | システムダイアログ、通知、複数アプリ |
| マネージドデバイス | src/androidTest/ | Gradle 管理の AVD | 約数分(初回実行時) | CI、API レベルをまたいだマトリックステスト |
詳しい例、コードパターン、Gradle Managed Device の設定については、テストを参照してください。
8.3 テストコマンド
# Local unit tests (fast, no emulator)
./gradlew test # all modules
./gradlew :app:testDebugUnitTest # app module, debug variant
# Single test class
./gradlew :app:testDebugUnitTest --tests "com.example.myapp.CounterViewModelTest"
# Instrumentation tests (requires device or managed device)
./gradlew connectedDebugAndroidTest # on connected device
./gradlew pixel6Api34DebugAndroidTest # on managed device
# Both together
./gradlew test connectedDebugAndroidTest
# Test with coverage report (JaCoCo)
./gradlew testDebugUnitTest jacocoTestReport