【Android + UIテスト】Hilt Moduleの一部バインディングをモックする
Hilt で Dependency Inject (DI)
みなさん Hilt 使ってますか? 手動による DI に比べコーディングが楽&テスト時のモックした依存との置換が楽、などメリット沢山です! まだの人は今すぐ始めよう
[Android Developer] Hilt を使用した依存関係の注入
UIテストで依存の置換
テストの基本的な方法はここでは説明しません。詳細は以下の記事が詳しいです。
[Android Developer] Hilt テストガイド
[Dagger] Hilt - Testing
状況の設定
次のようなMainViewModel
と依存関係があるとします。
@HiltViewModel
class MainViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val repository1: Repository1,
private val repository2: Repository2,
) : ViewModel() {
// some implementation
}
依存 Repository1, Repository2
はいずれもinterface
として定義されており、次のような Hilt Module から DI されるとします。
interface Repository1 {
// some definition
}
// ここではApplicationを通して同一インスタンスをバインディングするようスコープを設定する
@Singleton
class Repository1Impl @Inject constructor(
@ApplicationContext context: Context
) : Repository1 {
// some implmentation
}
@Module
@InstallIn(SingletonComponent::class)
interface RepositoryModule {
@Binds
fun bindRepository1(impl: Repository1Impl): Repository1
@Binds
fun bindRepository2(impl: Repository2Impl): Repository2
// other bindings
}
一部バインディングを置換
テストのためRepository2
のみモックしたいとします。テストクラスではモックしたインスタンスをDIするよう@BindValue
でHiltに指示します。そのままではバインディングがRepositoryModule
の@Binds
と重複してしまうので@UninstallModules
でモジュールの方を無視するよう指示を追加します。
@UninstallModules(RepositoryModule::class)
@HiltAndroidTest
@LargeTest
@RunWith(AndroidJUnit4::class)
class SomeActivityTest {
@Rule
@JvmField
val rule: RuleChain = RuleChain.outerRule(HiltAndroidRule(this))
.around(ActivityScenarioRule(SomeActivity::class.java))
// Mockk でテスト用にモックしたインスタンス
@BindValue
val repository2 = mockk<Repository2>(relaxed = true)
@Before
fun setup() {
// some setup code which must be run before each test unit
}
@Test
fun someTest() {
// test code here
}
}
しかし同時に Repository1
等の他バインディングも消えてしまうので、このままでは "error: [Dagger/MissingBinding] Repository1 cannot be provided ..." と怒られてしまいます。
調べた範囲では特定のバインディングのみ削除する方法はなく、モジュールごと@UninstallModules
とする以外にないようです。モック対象以外のバインディングを再度定義し直す必要がありますが、@BindValue
で列挙するのは面倒です。そこで、モック対象のバインディングのみ削除した新しいモジュールを再度@InstallIn
します。
@UninstallModules(RepositoryModule::class)
@HiltAndroidTest
@LargeTest
@RunWith(AndroidJUnit4::class)
class SomeActivityTest {
...
+ // ネストして定義したモジュールはこのテストクラスのみインストールされる
+ @Module
+ @InstallIn(SingletonComponent::class)
+ abstract class TestModule : RepositoryModule {
+ // モック対象のみバインディングを削除
+ override fun bindRepository2(impl: Repository2Impl): Repository2 = throw NotImplementedError()
+ }
...
}
@TestInstallIn
でも似たような方法が可能ですが、ネストしたモジュールの@InstallIn
と異なりすべてのテストに影響してしまうため選びませんでした
Hiltモジュールの設計は慎重に
そもそもモジュールを適切に分割するなどして@UninstallModules
で対応できれば発生しない問題です。機能単位で分割するなどテストに優しい設計を心がけたいです。
Author And Source
この問題について(【Android + UIテスト】Hilt Moduleの一部バインディングをモックする), 我々は、より多くの情報をここで見つけました https://qiita.com/Seo-4d696b75/items/2a26bd82967ae378af2a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .