---
# System prepended metadata

title: Unit Test - 使用hilt測試3 - setUp Architectural Skeleton
tags: [Unit Test, hilt, test, kotlin]

---

# Unit Test - 使用hilt測試3 - setUp Architectural Skeleton


---
## 這篇紀錄建立app的主要架構
* layout
* fragment
* viewModel


## 開始吧

![](https://i.imgur.com/AGhTseO.png)

### 創建fragment對應到各自的layout

```kotlin=

class ShoppingFragment: Fragment(R.layout.fragment_shopping) {}

class AddShoppingItemFragment: Fragment(R.layout.fragment_add_shopping_item) {}

class ImagePickFragment: Fragment(R.layout.fragment_image_pick) {}

```

### 建立ViewModel
* 這邊只用一個viewModel，其他fragment共享
* 用DI inject，注意這邊注入的是一個interface，
```kotlin=
@HiltViewModel
class ShoppingViewModel @Inject constructor(
    private val repository: ShoppingRepository,
) : ViewModel() {

}

```
* 在DI Module提供方法
* 注入介面[官方寫法](https://developer.android.com/training/dependency-injection/hilt-android)
* 作者在這邊使用as做型態轉換
```kotlin=
@Singleton
    @Provides
    fun provideDefaultShoppingRepository(
        dao: ShoppingDao,
        api: PixabayAPI
    ) = DefaultShoppingRepository(dao,api) as ShoppingRepository //轉成介面
    
```

### event管理的類別
* 作者提供了一個類別，避免當liveData處理事件之後，若發生旋轉銀幕...等等的configration change event造成liveData會再次處理一次
* 透過一個布林值來控制
```kotlin=
open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}
```

### 補完viewModel

```kotlin=
@HiltViewModel
class ShoppingViewModel @Inject constructor(
    private val repository: ShoppingRepository,
) : ViewModel() {

    val shoppingItems = repository.observeAllShoppingItems()

    val totalPrice = repository.observeTotalPrice()

    private val _images = MutableLiveData<Event<Resource<ImageResponse>>>()
    val image: LiveData<Event<Resource<ImageResponse>>> = _images

    private val _curImageUrl = MutableLiveData<String>()
    val curImageUrl: LiveData<String> = _curImageUrl

    private val _insertShoppingItemStatus = MutableLiveData<Event<Resource<ShoppingItem>>>()
    val insertShoppingItemStatus: LiveData<Event<Resource<ShoppingItem>>> =
        _insertShoppingItemStatus

    fun setCurImageUrl(url: String) {
        _curImageUrl.postValue(url)
    }

    fun deleteShoppingItem(shoppingItem: ShoppingItem) = viewModelScope.launch {
        repository.deleteShoppingItem(shoppingItem)
    }

    fun insertShoppingItemIntoDb(shoppingItem: ShoppingItem) = viewModelScope.launch {
        repository.insertShoppingItem(shoppingItem)
    }

    fun insertShoppingItem(name: String, amountString: String, priceString: String) {
        //要先寫測試，這邊暫不實作
    }

    fun searchForImage(imageQuery: String){
        //要先寫測試，這邊暫不實作
    }


}
```

### 把3個fragment 帶入相同的ViewModel
```kotlin=

class ShoppingFragment: Fragment(R.layout.fragment_shopping) {
    lateinit var viewModel: ShoppingViewModel

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel = ViewModelProvider(requireActivity()).get(ShoppingViewModel::class.java)
    }
}

class AddShoppingItemFragment: Fragment(R.layout.fragment_add_shopping_item) {
    lateinit var viewModel: ShoppingViewModel

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel = ViewModelProvider(requireActivity()).get(ShoppingViewModel::class.java)
    }

}

class ImagePickFragment: 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)
    }

}
    
```
## 前置作業完成，下一篇就開始來做viewModel測試了

參考資料
[Philipp Lackner's channel](https://www.youtube.com/watch?v=x2WahC3N_Yw)

###### tags: `test` `Unit Test` `hilt` `kotlin`