JUnitによるユニットテスト
JUnit 4で高速かつ信頼性の高いユニットテストを記述します。@Test、@Before、@After、アサーションを使い、InstantTaskExecutorRuleでLiveDataを、runTestでコルーチンをテストします。
「JUnitによるユニットテスト」はCoddyKit上の無料Android Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAndroid Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Android Academyコースには全4レッスンが含まれています。
なぜテストを書くのですか?
テストはぜいたく品ではなく、コードを守る安全網です:
- ユーザーが見つける前にバグを発見できます
- 安心してリファクタリングできます(テストが通れば、動作が壊れていないと確認できます)
- 意図した動作を文書化できます
- 機能追加時のデグレードを防げます
十分にテストされたコードは、設計も優れている傾向があります。テストが難しい場合は、設計の結合度が高すぎる可能性があります。
Androidのテストの種類
テストには2つのカテゴリがあります:
- Unit tests(
test/フォルダー) — JVM上で実行するため、デバイスは不要です。高速で、純粋なKotlin/Javaのロジックをテストします。 - Instrumented tests(
androidTest/フォルダー) — デバイスまたはエミュレーター上で実行します。時間はかかりますが、Android固有のコード(Room DB、UI)をテストできます。
ビジネスロジックには、できるだけユニットテストを使用してください。Android APIとのやり取りが必要な場合にのみ、インストルメンテーションテストを使用します。
JUnit 4のセットアップ
JUnit 4はAndroidプロジェクトにデフォルトで含まれています。app/build.gradleに次を追加します:
// app/build.gradle:
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
testImplementation 'androidx.arch.core:core-testing:2.2.0' // for LiveData
}最初のJUnitテスト
src/test/java/にクラスを作成し、テストメソッドに@Testを付けます:
import org.junit.Test
import org.junit.Assert.*
class CalculatorTest {
private val calc = Calculator()
@Test
fun `addition returns correct sum`() {
val result = calc.add(2, 3)
assertEquals(5, result)
}
@Test
fun `division by zero throws exception`() {
assertThrows(ArithmeticException::class.java) {
calc.divide(10, 0)
}
}
}@Beforeと@After
ライフサイクルアノテーションを使用して、各テストの前後に状態をセットアップおよび破棄します:
class UserRepositoryTest {
private lateinit var repo: UserRepository
private lateinit var fakeDao: FakeUserDao
@Before
fun setUp() {
fakeDao = FakeUserDao() // fresh instance before each test
repo = UserRepository(fakeDao)
}
@After
fun tearDown() {
fakeDao.clear() // clean up after each test
}
@Test
fun `getUsers returns all users`() {
fakeDao.insert(User(1, "Alice"))
fakeDao.insert(User(2, "Bob"))
assertEquals(2, repo.getUsers().size)
}
}よく使うアサーション
JUnitにはAssert.*のアサーションメソッドが用意されています:
// Equality:
assertEquals(expected, actual)
assertNotEquals(unexpected, actual)
// Null:
assertNull(value)
assertNotNull(value)
// Boolean:
assertTrue(condition)
assertFalse(condition)
// Same object reference:
assertSame(expected, actual)
// Custom message on failure:
assertEquals("User ID should be 1", 1, user.id)
// Exception:
assertThrows(IllegalArgumentException::class.java) {
User(id = -1, name = "")
}ViewModelのテスト
LiveDataをテストするには、LiveDataの投稿を同期的に実行するInstantTaskExecutorRuleを追加します:
class UserViewModelTest {
@get:Rule
val instantTaskRule = InstantTaskExecutorRule()
private val fakeRepo = FakeUserRepository()
private lateinit var viewModel: UserViewModel
@Before
fun setUp() {
viewModel = UserViewModel(fakeRepo)
}
@Test
fun `users are loaded on init`() {
fakeRepo.usersToReturn = listOf(User(1, "Alice"), User(2, "Bob"))
viewModel.loadUsers()
val result = viewModel.users.value
assertNotNull(result)
assertEquals(2, result?.size)
}
}コルーチンのテスト
TestCoroutineDispatcher/StandardTestDispatcherを使用して、テスト中のコルーチンの実行を制御します:
class SyncWorkerTest {
@get:Rule
val instantTaskRule = InstantTaskExecutorRule()
private val testDispatcher = StandardTestDispatcher()
@Before
fun setUp() {
Dispatchers.setMain(testDispatcher)
}
@After
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `sync completes successfully`() = runTest {
val repo = FakeRepository()
val worker = SyncUseCase(repo, testDispatcher)
val result = worker.run()
assertTrue(result.isSuccess)
}
}FakeとMock
テストで実際の依存関係を置き換える方法は2つあります:
- Fake — テスト用に作られた、動作する実装です(例:実際のデータベースの代わりにメモリ上のリストを使用する)。作成しやすく読みやすいうえ、ライブラリも必要ありません。
- Mock — 呼び出しを記録し、やり取りを検証できる自動生成のスタブです(Mockito、MockK)。より強力ですが、使いすぎると壊れやすくなります。
単純なケースではFakeを優先し、メソッド呼び出しの検証が必要な場合はMockを使用してください。
Fakeの作成
Fakeは実際のクラスと同じインターフェースを実装しますが、メモリ上にデータを保存します:
interface UserDao {
fun getAll(): List<User>
fun insert(user: User)
}
class FakeUserDao : UserDao {
private val storage = mutableListOf<User>()
override fun getAll(): List<User> = storage.toList()
override fun insert(user: User) { storage.add(user) }
fun clear() { storage.clear() }
}テスト名の規約
適切なテスト名は、何をテストしているかと期待される結果を表します。Kotlinでは、読みやすい説明にするため、バッククォートで囲んだ関数名を使用します:
`getUsers returns empty list when no users exist``login throws exception when password is blank``add returns correct sum for negative numbers`
パターン:`subject does X when Y`
確認問題
JUnitのテストクラスにおける@Beforeの役割は何ですか?
まとめ:JUnitによるユニットテスト
信頼できるアプリは、テストされたアプリです:
- ユニットテストは
src/test/に配置し、JVM上で高速に実行します @Testを付け、assertEquals、assertTrue、assertThrowsを使用します- セットアップと破棄には
@Before/@Afterを使用します - LiveDataを同期的にテストするには
InstantTaskExecutorRuleを使用します - コルーチンのテストには
runTest+StandardTestDispatcherを使用します - 単純な置き換えにはMockよりFakeを優先します
次は、やり取りを検証するためのMockitoによるモックです。
よくある質問
「JUnitによるユニットテスト」レッスンは無料ですか?
はい。「JUnitによるユニットテスト」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Android Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Android Academyコースには全4レッスンが含まれています。
「JUnitによるユニットテスト」で何を学びますか?
JUnit 4で高速かつ信頼性の高いユニットテストを記述します。@Test、@Before、@After、アサーションを使い、InstantTaskExecutorRuleでLiveDataを、runTestでコルーチンをテストします。 ブラウザで直接実行するハンズオンコードでAndroid Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Android Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAndroid Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「JUnitによるユニットテスト」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAndroid Academyレッスンでコードを書いて実行できますか?
はい。すべてのAndroid Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- JUnitによるユニットテスト
- Mockitoによるモック
- EspressoによるUIテスト
- デバッグとプロファイリング