DI testing, graph validation, startup, and leaks
Test behavior without a graph when possible
val vm = CheckoutViewModel(
savedStateHandle = SavedStateHandle(),
placeOrder = FakePlaceOrder(result = acceptedOrder),
)
Constructor injection makes unit tests cheap. Use Hilt tests when the test question is wiring, Android integration, qualifiers, replacements, or scopes.
Replace bindings deliberately
Test modules can uninstall/replace production modules or bind test values. Keep replacements narrow; a giant test graph that silently swaps many dependencies can pass while production wiring is wrong.
Useful graph tests:
- production variants compile all roots
- each qualifier resolves expected implementation
- scoped identity is same within scope and different across scope instances
- optional/multibound plugins are present exactly where intended
- debug/release/flavor graphs do not accidentally expose test endpoints
Startup cost is often provider work
Dagger-generated lookup is normally cheap; constructors/provider methods can be expensive. Avoid database opening, disk reads, key generation, SDK initialization, or network work merely because a singleton is requested at startup.
Use lazy/provider only when delayed or repeated creation semantics are understood:
class Feature @Inject constructor(private val sdk: dagger.Lazy<HeavySdk>) {
fun open() = sdk.get().show()
}
Lazy caches once for that injected Lazy; Provider.get() may create according to binding scope each call.
Scope leak scenarios
- singleton holds Activity, View, NavController, or callback
- ActivityRetained object holds old Activity after rotation
- flow component remains in Application after logout
- listener registration outlives component disposal
- multibound startup task captures feature UI
Memory profiler/LeakCanary finds symptoms; scope design prevents them.
Build performance
Large generated graphs affect annotation processing and incremental builds. Prefer constructor injection, avoid monolithic modules/components, keep module dependencies acyclic, use supported KSP/tooling where appropriate, and measure build scans rather than guessing.
Security/configuration
Qualifiers can accidentally wire debug endpoint, logging interceptor, or mock auth into release. Add variant-specific graph tests and release configuration assertions. DI is configuration code and deserves security review.
A compiling graph proves constructibility, not correct scope, endpoint, thread behavior, privacy, or business semantics. Those require targeted tests and runtime evidence.
Interview exercises
- Investigate slow startup caused by eager singleton construction.
- Write a scope identity test across two Activity instances.
- Prevent debug network bindings from entering release.