Android Basic with ComposeのUnit2で学んだことをまとめてみる - 前編(Kotlin/Test)
はじめに
こんにちは、某SIerでSEをやっているnekorush14です。
この記事は絶賛再*n入門しているAndroid開発について、Google公式のLearning Courseを通して学んだことをアウトプットするシリーズです。
Jetpack Composeに関するCourseのUnit2で得た知見を話します。
前回の記事はこちら👇
Unit 2: Building app UI
このユニットでは、シンプルなユーザーインタラクションを含むアプリケーションの実装を通して以下の内容を習得します。
- Kotlinでの分岐、オブジェクト指向プログラミング、Null許容、関数型の扱い
- ボタンとボタンをタップしたときのアクション定義の仕方
- Androidの状態管理の基礎
- Androidにおけるデバッグとテストの基礎
Kotlinについて少し忘れているところもあったので、今回は短いですがKotlinの章で学んだことも少しだけまとめてみます。
また、Unit2のメインテーマはユーザーインタラクションを含むアプリケーションの仕組みと状態管理(だと思っています)です。今回はUnit2で扱われたModifier、Compose、テスト、そしてステータス管理についてまとめてみます。加えて、Unitの最後の課題で取り組んだアプリケーション作成にて、このUnitのスコープ外のことも少しだけ取り入れてみたのでその話もしてみます。
Kotlin
このユニットでは以下のテーマが扱われました。
- Generics
- Data class
- Singleton Object
- Companion object
- 継承なしでクラスを拡張するExtensions
- Collections
この中で気になったものはData class, Companion object, Extensionsです。
Data classはクラスのプロパティを使ってデータを保持するために使用されるクラスです。このクラスのequals()
やtoString()
は自動的に生成されます。特にtoString
は通常のクラスと異なり、オブジェクトのハッシュではなくクラスのプロパティを文字列で返します。
※便利ですね🤔
CCompanion objectはクラスに紐づいたシングルトンオブジェクトを生成するためのものです。 object
宣言でもシングルトンオブジェクトを生成できますが、Companion objectを使うことで、クラス名を通してシングルトンオブジェクトにアクセスできます。Companion objectの場合は<class名>.<オブジェクト内のプロパティまたはメソッド名>
でアクセスできます。
※Javaで言うStaticな値やメソッドみたいなイメージで使う理解です。
詳しくは、Kotlinの公式ドキュメントData classes | Kotlin Documentationを参照ください。
Extensionsは継承なしでクラスを拡張する構文です。
例えば以下のようなイメージです。
class Sample
/**
* [Sample]クラス宣言時に定義されていない`getSample`メソッドを定義する。
*/
fun Sample.getSample(): String {
// ...
}
fun main() {
val sample = Sample()
// 呼び出しはこの例だとSampleオブジェクトを作成してから通常のメソッド呼び出しと同じ記法で
// メソッドを呼び出せる
println(sample.getSample())
}
Codelabsによると、Jetpack ComposeではModifierがExtensionsを使用して実装されているそうです。
Test
Androidで提供されている標準のテストは以下の2つです。
- Local test
- Instrumentation test
Local testはいわゆるUnit Testと同義で、作成した関数内のロジック・クラス・プロパティなどをテストします。
JUnit
を使用しているため、Android Studioで完結し、かつテスト実行にエミュレータやAndroid実機は不要です。
Instrumentation testは直訳すると機器テストです。Androidにおいては作成したUIのテスト、Android固有のAPIやプラットフォームのAPI/サービスに依存するテストを意味します。
UIやAndroidがサポートするAPIに絡んだテストを行うため、テストの実行にエミュレータやAndroid実機が必要です。
Instrumentation testを実行すると、Android Studioにおけるエミュレータでのアプリ実行時と同様にAPK[1]にパッケージングされます。
通常のアプリケーションAPKに加えてテスト用のAPKも生成されます。テストを実行するデバイスには通常のアプリケーションAPKがインストールされ、テスト用のAPKが通常のアプリケーションAPKに対してテストを実行します。
テストを記載したコード(テストコード)からテスト対象のコード(プロダクトコード)へアクセスする際、プロダクトコードがprivate
で宣言されているとアクセスできません。
※private
なメソッドはテストすべきなのかという議論がありますが、ここではスコープ外とします。
そこで、このCodelabsではprivate
で宣言されている箇所をinternal
へ変更し、@VisibleForTesting
アノテーションを付与することでこの問題を回避しています。
@VisibleForTesting
で宣言されたメソッドを、テスト目的でのみ外部からアクセスできるようにすることを明示しています。
Local test
Local testのテストディレクトリはsrc/test/java/<PackageName>/
に作成します。
テストコードの階層はプロダクトコードコードと一致化させる必要があるため、以下のようにmain
配下と同様のディレクトリ構成とします。
Project view表示
テストコードはまずテストクラスを作成します。テストクラス内でテストケースごとにテストメソッドを作成します。
テストメソッドには@Test
アノテーションを付与します。実行結果が予期した値となっているか確認するアサーションですが、Androidのテストではassert
メソッドを使用します。
基本的にはJUnit
を使用しているため以下のようなアサーションが使用できます。
-
assertEquals()
- 引数の値どおししが一致しているか -
assertNotEquals()
- 引数の値どおしが一致していないか -
assertTrue()
- 引数の値がTrueであるか -
assertFalse()
- 引数の値がFalseであるか -
assertNull()
- 引数の値がNullか -
assertNotNull()
- 引数の値がNull以外か -
assertThat()
-Hamcrest
ライブラリ等を使用したMatcherによるアサーション
テストの実行はIDEのガターにあるアイコンから実行する。
この際、Debug実行も可能です。つまりプロダクトコードにBreakPointを付与して値を確認できます。
class SampleTests {
@Test
fun sampleCalculation_sumReturnString() {
val value1 = 10.00
val value2 = 20.00
val expectedString = "30.00"
val actualString = sampleCalculation(x = value1, y = value2)
assertEquals(expectedString, actualString)
}
}
Instrumentation test
Instrumentation testのテストディレクトリはsrc/androidTest/java/<PackageName>
に作成します。
テストコードの階層はプロダクトコードコードと一致化させる必要があるため、以下のようにmain
配下と同様のディレクトリ構成とします。
Project view表示
Instrumentation testのテストクラスもLocal Testと同様に作成します。
注意点は、UIテストのため、onCreate()
が呼ばれてUIが設定されたあとにテストを実行する必要があります。
テストコード内でcreateComposeRule()
を変数に格納し、createComposeRule().setContent
によりプロダクトコードのUIを設定します。
UIのコンポーネントにテストコードからアクセスするにはノードを通して行います。
onNodeWithText()
により引数のStringを持つコンポーネントにアクセスできます。
また、performTextInput()
により引数に指定した値をそのコンポーネントに渡すことができます。
ノードを通してアクセスしたコンポーネントから直接assertするメソッドを使用できます。
このassert
メソッドの引数はアサーションがFalseとなる場合のメッセージを入れます。
class SampleUITests {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun calculate_sample_30_value() {
composeTestRule.setContent {
SampleTheme {
Surface (modifier = Modifier.fillMaxSize()){
SampleLayout()
}
}
}
composeTestRule.onNodeWithText("Sample X").performTextInput("10")
composeTestRule.onNodeWithText("Sample Y").performTextInput("20")
val expectedValue = "30"
composeTestRule.onNodeWithText("Sample Value: $expectedValue").assertExists(
"No node with this text was found."
)
}
}
まとめ
今回はUnit2で扱われたKotlinとAndroidでのTestについてまとめてみました。
Androidでリストなどを使用する際に必要となる知識やデータを扱う際に使用するであろうクラスについてKotlinの記法に触れられていました。
特にDataクラスやCollectionsの話はデータを扱う際に基本となると考えています。
TestではAndroidにおける単体テストとUIテストの書き方について基礎的なことを学べました。
次回も引き続きUnit2で、メイントピックとなるComposeの状態管理についてまとめます。
参考資料
- Android Basics with Compose course | Android Developers
- Android Basics with Compose | Building App UI | Android Developers
- Data classes | Kotlin Documentation
-
APK(Android Application Package)はその名の通り、Androidのアプリケーションをパッケージ化したファイルです。 ↩︎
Discussion