# Unit Test - 使用hilt測試8 - Testing Image Picking
---
## 開始吧
### 情境
* 在這裡要驗證當選取了圖片之後是否有正確將url設定至變數
* 圖片是顯示在recycler view,用到Espresso對recyclerView操作
### 建立adapter吧
```kotlin=
class ImageAdapter @Inject constructor(
var glide: RequestManager
) : RecyclerView.Adapter<ImageAdapter.ImageViewHolder>() {
class ImageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
//效能取捨使用 diffUtil,址更新有變動的項目
private val diffCallBack = object : DiffUtil.ItemCallback<String>() {
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}
}
private val differ = AsyncListDiffer(this, diffCallBack)
var images: List<String>
get() = differ.currentList
set(value) = differ.submitList(value)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
return ImageViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_image,
parent,
false
)
)
}
private var onItemClickListener: ((String) -> Unit)? = null
fun setOnItemClcinListener(listener: (String) -> Unit) {
onItemClickListener = listener
}
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
val url = images[position]
holder.itemView.apply {
glide.load(url).into(ivShoppingImage)
setOnClickListener {
onItemClickListener?.let { click ->
click(url)
}
}
}
}
override fun getItemCount(): Int {
return images.size
}
}
```
### 建立FragmentFactory
* 在ImagePickFragment注入adapter
```kotlin=
class ImagePickFragment @Inject constructor(
private val imageAdapter: ImageAdapter
): Fragment(R.layout.fragment_image_pick) {
}
```
* override Fragment Factory
* 當實例化的class是ImagePickFragment,要傳入adapter這參數,若是其他類別就是預設的super方法
```kotlin=
class ShoppingFragmentFactory @Inject constructor(
private val imageAdapter: ImageAdapter
) : FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
return when (className) {
ImagePickFragment::class.java.name -> ImagePickFragment(imageAdapter)
else -> super.instantiate(classLoader, className)
}
}
}
```
### 初始化recycler View & click listener
```kotlin=
class ImagePickFragment @Inject constructor(
private val imageAdapter: ImageAdapter
) : Fragment(R.layout.fragment_image_pick) {
lateinit var viewModel: ShoppingViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(requireActivity()).get(ShoppingViewModel::class.java)
setupRecyclerView()
imageAdapter.setOnItemClcinListener {
//當選取圖片之後,就會退回上一頁
findNavController().popBackStack()
viewModel.setCurImageUrl(it)
}
}
private fun setupRecyclerView() {
rvImages.apply {
adapter = imageAdapter
layoutManager = GridLayoutManager(requireContext(), GRID_SPAN_COUNT)
}
}
}
```
### 開始寫測試吧
* gradle新增espresso支援recyclerView操作
```kotlin=
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.3.0'
```
* 如果測試到的元件有動畫效果,要關閉,他會干擾espresso
* 一個方法是用adb指令,在terminal分三次輸入
1. adb shell settings put global window_animation_scale 0.0
2. adb shell settings put global transition_animation_scale 0.0
3. adb shell settings put global animator_duration_scale 0.0
* 另一個方法是設定gradle
```kotlin=
//gradle
android {
testOptions {
animationsDisabled = true
}
}
```
* 測試主體
```kotlin=
@HiltAndroidTest
@MediumTest
@ExperimentalCoroutinesApi
class ImagePickFragmentTest{
//有使用到背景執行緒就要加入這個rule
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
@get:Rule
var hiltRule = HiltAndroidRule(this)
//注入我們定義的factory,侷限在image pick fragment才注入adapter
@Inject
lateinit var fragmentFactory: ShoppingFragmentFactory
@Before
fun setup(){
hiltRule.inject()
}
@Test
fun clickImage_popBackStackAndSetImageUrl(){
val navController = mock(NavController::class.java)
val imageUrl = "test Url"
val testViewModel = ShoppingViewModel(FakeRepository())
launchFragmentInHiltContainer<ImagePickFragment>(fragmentFactory = fragmentFactory) {
Navigation.setViewNavController(requireView(),navController)
//傳遞資料給adapter
imageAdapter.images = listOf(imageUrl)
viewModel = testViewModel
}
onView(withId(R.id.rvImages)).perform(
RecyclerViewActions.actionOnItemAtPosition<ImageAdapter.ImageViewHolder>(
0,
click()
)
)
verify(navController).popBackStack()
//驗證cuurImageUrl是否跟我們設定給他的一樣
assertThat(testViewModel.curImageUrl.getOrAwaitValue()).isEqualTo(imageUrl)
}
}
```

參考資料
[Philipp Lackner's channel](https://www.youtube.com/watch?v=IcceHTRi7vg&t=36s)
###### tags: `test` `Unit Test` `kotlin` `hilt`