Espresso 不会等到 RecyclerView 从 LiveData 获取更新

Espresso doesn't wait till RecyclerView gets update from LiveData

StatsFragment 包含 recyclerview。 recylcerView 显示“Stat”。 recylcerView 适配器通过观察 ViewModel 中的 LiveData 来更新。 ViewModel 通过 Flow 从存储库获取更新。 在 StatsFragment 中是一个 FAB,它打开一个 DialogFragment,可以添加一个新的 Stat。

我怀疑问题是 espresso 没有等到适配器更新并且新数据显示在屏幕上。

如何让 espresso 等到存储库流发出它的值,将其传递给 viewModels LiveData,以便更新片段中的列表以通知适配器在 recyclerview 中显示列表?

StatsFragment

class StatsFragment : Fragment() {

    private lateinit var binding: FragmentStatsBinding
    private val mainViewModel: MainViewModel by viewModel()

    private lateinit var recyclerView: RecyclerView
    private lateinit var statsAdapter: StatsAdapter
    private lateinit var layoutManager: LinearLayoutManager

    private lateinit var addStats: FloatingActionButton

    private lateinit var addStatFragment: DialogFragment

    private val stats = mutableListOf<Stat>()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentStatsBinding.inflate(inflater, container, false)
        binding.lifecycleOwner = viewLifecycleOwner
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initObserver()
        initUi()
    }

    private fun initObserver() {
        mainViewModel.stats.observe(viewLifecycleOwner, Observer { statList ->
            stats.clear()
            stats.addAll(statList)
            statsAdapter.notifyDataSetChanged()
        })
    }

    private fun initUi() {
        statsAdapter = StatsAdapter(stats)
        layoutManager = LinearLayoutManager(requireContext())
        recyclerView = binding.recyclerViewStatsData
        recyclerView.layoutManager = layoutManager
        recyclerView.adapter = statsAdapter

        addStats = binding.fabStatsAddStat
        addStats.setOnClickListener(addStatOnClickListener)
    }

    private val addStatOnClickListener = View.OnClickListener {
        addStatFragment = AddStatFragment()
        addStatFragment.show(requireActivity().supportFragmentManager, "addStatFragment")
    }

}

AddStatFragment

class AddStatFragment : DialogFragment() {

    private lateinit var binding: FragmentInputBinding
    private val mainViewModel: MainViewModel by viewModel()

    private lateinit var date: TextView
    private lateinit var time: TextView

    private lateinit var confirm: Button

    override fun onStart() {
        super.onStart()
        val width = resources.displayMetrics.widthPixels
        val height = resources.displayMetrics.heightPixels
        dialog!!.window?.setLayout(width, WRAP_CONTENT)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentInputBinding.inflate(inflater, container, false)
        binding.viewModel = mainViewModel
        binding.lifecycleOwner = viewLifecycleOwner
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initUi()
    }

    private fun initUi() {
        date = binding.textViewInputDate
        time = binding.textViewInputTime
        confirm = binding.buttonInputConfirm

        confirm.setOnClickListener {
            mainViewModel.insertStat()
            dialog!!.hide()
        }
    }
}

MainViewModel

class MainViewModel(private val repository: Repository, application: Application) :
    AndroidViewModel(application) {

    private val _stats = repository.getAllStats().asLiveData()
    val stats: LiveData<List<Stat>>
        get() = _stats

    private val _insertStatStatus = MutableLiveData<Event<Resource<Stat>>>()
    val insertStatStatus: LiveData<Event<Resource<Stat>>>
        get() = _insertStatStatus

    val weight = MutableLiveData<String>()
    val waist = MutableLiveData<String>()
    val kCal = MutableLiveData<String>()
    val date = MutableLiveData<String>()
    val time = MutableLiveData<String>()

    private fun insertStatIntoDatabase(stat: Stat) {
        viewModelScope.launch {
            repository.insertStat(stat)
        }
    }

    internal fun insertStat(weight: String, waist: String, kCal: String, date: String, time: String) {
        val newStat = Stat(weight.toDouble(), waist.toDouble(), kCal.toDouble(), date, time)
        insertStatIntoDatabase(newStat)
        _insertStatStatus.value = Event(Resource.success(newStat))
    }


    internal fun insertStat() {
        insertStat(weight.value ?: "", waist.value ?: "", kCal.value ?: "", date.value ?: "", time.value ?: "")
    }
}

FakeRepository

class FakeStatsRepositoryAndroidTest: Repository {

    private val fakeStats = MutableLiveData<MutableList<Stat>>()
    private val underlyingList = mutableListOf<Stat>()
    private var idCounter = 1

    init {
        fakeStats.value = underlyingList
    }

    override fun getAllStats(): Flow<List<Stat>> {
        return fakeStats.asFlow()
    }

    override suspend fun insertStat(stat: Stat) {

        val newStat = Stat(stat.weight,stat.waist,stat.kCal,stat.date,stat.time,idCounter)
        idCounter++
        underlyingList.add(newStat)
    }

    override suspend fun deleteStat(stat: Stat) {
        underlyingList.remove(stat)
    }

    override suspend fun updateStat(stat: Stat) {
        if(underlyingList.removeIf { it.id == stat.id }) {
            underlyingList.add(stat)
        } else {
            return
        }
    }
}

测试

    @Test
    fun openingAddStatFragmentWhenEnteringInformationAndPressingConfirmThenStatIsVisibleInList() {
        val statToAdd = Stat(70.1,88.5,2300.0,"today", "now",id = 1)

        onView(withId(R.id.fab_stats_addStat)).perform(click())
        onView(withId(R.id.textInputEditText_input_weight)).perform(typeText(statToAdd.weight.toString()))
        onView(withId(R.id.textInputEditText_input_waist)).perform(typeText(statToAdd.weight.toString()))
        onView(withId(R.id.textInputEditText_input_kcal)).perform(typeText(statToAdd.weight.toString()))
        onView(withId(R.id.button_input_confirm)).perform(click())
        onView(withId(R.id.recyclerView_stats_data)).perform(RecyclerViewActions.scrollTo<StatsViewHolder>(hasDescendant(withText("70.1"))))
    }

我删除了一些代码(输入值检查、时间和日期相关的东西等)

错误在别处。没有来自 Fake 存储库的任何数据,因此 liveData 没有更新。