自動テストの実行環境
同じマシン上で実行
Emulator 実機
Instrumentation Test
Robolectric
Robolectric
ではない
Local Test
Local Test
Slide 33
Slide 33 text
自動テストの実行環境
Emulator 実機
Robolectric
Robolectric
ではない
Local Test
Local Test
Instrumentation Test
今回は実機とEmulator
の区分は重要ではない
のでまとめて考える
同じマシン上で実行
Slide 34
Slide 34 text
自動テストの実行環境
Emulator 実機
Robolectric
Robolectric
ではない
Local Test
Instrumentation Test
同じマシン上で実行
Local Testの2つは分けて考える
以降、RobolectricではないLocal Testは
Pure Local Testとする
Slide 35
Slide 35 text
実行環境ごとの特徴
Instrumentation Test
Robolectric
Pure Local Test
速い
遅い
実行速度
実環境への
近さ
遠い
近い
Slide 36
Slide 36 text
実環境への近さ
● Pure Local TestではAndroidフレームワークのコードを呼び出すこと
はできないが、RobolectricとInstrumentation Testはできる
● Robolectricはフレームワークのコードが動かない箇所を独自の実装
に置き換わるため、実環境への近さはInstrumentaion Testのほうが
近い
● 以前UIテストはInstrumentaion Testでしかできなかったが、
RobolectricでもInstrumentaion Testと同じテスト実装で同じテスト
結果を得られるような実装が進められてきた
フェイク
interface UserDataSource {
suspend fun getUser(): User
suspend fun saveUser(user: User)
}
// テスト対象メソッド Userを保存し、最新のUserを取得する関数
suspend fun updateUser(user: User) : User {
userDataSource.saveUser(user)
return userDataSource.getUser()
}
Slide 80
Slide 80 text
フェイク
// スタブ・スパイを使ったテストのイメージ
val user = User.filledUser()
val userDataSource = mockk {
coEvery { getUser() } returns user
}
val updatedUser = sut.updateUser(user)
coVerify {
userDataSource.saveUser(user)
}
assertEquals(User.filledUser(), updatedUser)
Slide 81
Slide 81 text
フェイク
// UserDataSourceのFake実装
class FakeUserDataSource : UserDataSource {
private var user: User = User.emptyUser()
override suspend fun getUser(): User {
return user
}
override suspend fun saveUser(user: User) {
this.user = user
}
}
Slide 82
Slide 82 text
フェイク
// Fakeを使ったテストのイメージ
val userDataSource = FakeUserDataStore()
val updatedUser = sut.updateUser(User.filledUser())
assertEquals(User.filledUser(), updatedUser)
Slide 83
Slide 83 text
フェイク
// Fakeを使ったテストのイメージ
val userDataSource = FakeUserDataStore()
val updatedUser = sut.updateUser(User.filledUser())
assertEquals(User.filledUser(), updatedUser)
テスト対象の公開されたAPIを使ったテストに
なっているため、メソッドの中の変更に強い
Slide 84
Slide 84 text
フェイク
// スタブ・スパイを使ったテストのイメージ
val user = User.filledUser()
val userDataSource = mockk {
coEvery { getUser() } returns user
}
val updatedUser = sut.updateUser(user)
coVerify {
userDataSource.saveUser(user)
}
assertEquals(user, updatedUser)
スタブとスパイを使ったテストコードは
テスト対象の実装の内部を見ている状態
テストデータの生成をまとめる
// 保存ボタンが活性化する条件をテストしたい
data class UiState(
val user: User
) {
val isSaveButtonEnable = user.lastName.isNotEmpty()
&& user.firstName.isNotEmpty()
}
Slide 122
Slide 122 text
テストデータの生成をまとめる
val sut = UiState(
user = User( // 各テストで直接Userのインスタンスを作っている場合
lastName = "lastName",
firstName = "firstName",
birthDate = null, height = 0f, weight = 0f
)
)
assertTrue(uiState.isSaveButtonEnable)
Slide 123
Slide 123 text
テストデータの生成をまとめる
val sut = UiState(
user = User( // 各テストで直接Userのインスタンスを作っている場合
lastName = "lastName",
firstName = "firstName",
birthDate = null, height = 0f, weight = 0f
)
)
assertTrue(uiState.isSaveButtonEnable)
Userにフィールドが追加されたら、Userを生成し
ている全てのテストがコンパイルエラーになる
Slide 124
Slide 124 text
テストデータの生成をまとめる
val sut = UiState(
user = emptyUser.copy( // 共通のユーザー定義を使う場合
lastName = "lastName",
firstName = "firstName",
)
)
assertTrue(uiState.isSaveButtonEnable)
Slide 125
Slide 125 text
テストデータの生成をまとめる
val sut = UiState(
user = emptyUser.copy( // 共通のユーザー定義を使う場合
lastName = "lastName",
firstName = "firstName",
)
)
assertTrue(uiState.isSaveButtonEnable)
テストに関係のあるフィールドだけセットする
Slide 126
Slide 126 text
テストデータの生成をまとめる
val sut = UiState(
user = emptyUser.copy( // 共通のユーザー定義を使う場合
lastName = "lastName",
firstName = "firstName",
)
)
assertTrue(uiState.isSaveButtonEnable)
Userにフィールドが増えても修正するのは共通の定義だけで良い
Slide 127
Slide 127 text
テスト対象や依存オブジェクトの生成をまとめる
// テスト対象クラス
class UserViewModel(
val profileRepository: UserRepository,
val userConfigRepository : UserConfigRepository
)
テスト対象や依存オブジェクトの生成をまとめる
// テスト対象クラス
class UserViewModel(
val profileRepository: UserRepository,
val userConfigRepository : UserConfigRepository,
val logTracker : LogTracker, // New
)
Slide 130
Slide 130 text
テスト対象や依存オブジェクトの生成をまとめる
// テスト対象クラス
class UserViewModel(
val profileRepository: UserRepository,
val userConfigRepository : UserConfigRepository,
val logTracker : LogTracker, // New
)
テストケース毎にコンストラクタの指定をして
いるとそれぞれ修正する必要があるが、生成を
1つにまとめていれば1箇所修正すればよい