问题
This is my viewmodel class :
class MainViewModel(
private val schedulerProvider: BaseSchedulerProvider,
private val api : StorytelService
) : BaseViewModel() {
private val _posts = MutableLiveData<List<Post>>()
val posts: LiveData<List<Post>>
get() = _posts
private val _status = MutableLiveData<Status>()
val status: LiveData<Status>
get() = _status
init {
showPhotos()
}
fun showPhotos() {
EspressoIdlingResource.increment() // App is busy until further notice
_status.postValue(Status.LOADING)
compositeDisposable.add(api.getPhotos()
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.doFinally {
if (!EspressoIdlingResource.countingIdlingResource.isIdleNow) {
EspressoIdlingResource.decrement() // Set app as idle.
}
}
.subscribe({
_status.postValue(Status.SUCCESS)
showPosts(it)
}) {
_status.postValue(Status.ERROR)
Timber.e(it)
})
}
private fun showPosts(networkPhotos: List<NetworkPhoto>) {
EspressoIdlingResource.increment() // App is busy until further notice
_status.postValue(Status.LOADING)
compositeDisposable.add(api.getPosts()
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.doFinally {
if (!EspressoIdlingResource.countingIdlingResource.isIdleNow) {
EspressoIdlingResource.decrement() // Set app as idle.
}
}
.subscribe({ networkPosts ->
_status.postValue(Status.SUCCESS)
_posts.postValue(
PostAndImages(networkPosts, networkPhotos).asDomaineModel()
)
}) {
_status.postValue(Status.ERROR)
Timber.e(it)
})
}
This is my recyclerView in layout :
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:showData="@{vm.status}"
tools:listitem="@layout/post_item" />
And here is binding adapter :
@BindingAdapter("showData")
fun View.showData(status: Status) {
visibility = if (status == Status.SUCCESS) View.VISIBLE else View.GONE
}
As you notice I am using EspressoIdlingResource, but when I run following espresso test, it fails :
@Test
fun shouldBeAbleToLoadList() {
onView(withId(R.id.recycler_view)).check(matches(isDisplayed()))
}
If I add Thread.sleep(5000) in the beginning of the test, it works. How to resolve it?
回答1:
It should be possible with Idling Resource
however they are a little bit tedious.
I've just updated an old viewMatcher code:
/**
* Perform action of waiting for a specific view id to be displayed.
* @param viewId The id of the view to wait for.
* @param millis The timeout of until when to wait for.
*/
public static ViewAction waitDisplayed(final int viewId, final long millis) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isRoot();
}
@Override
public String getDescription() {
return "wait for a specific view with id <" + viewId + "> has been displayed during " + millis + " millis.";
}
@Override
public void perform(final UiController uiController, final View view) {
uiController.loopMainThreadUntilIdle();
final long startTime = System.currentTimeMillis();
final long endTime = startTime + millis;
final Matcher<View> matchId = withId(viewId);
final Matcher<View> matchDisplayed = isDisplayed();
do {
for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
if (matchId.matches(child) && matchDisplayed.matches(child)) {
return;
}
}
uiController.loopMainThreadForAtLeast(50);
}
while (System.currentTimeMillis() < endTime);
// timeout happens
throw new PerformException.Builder()
.withActionDescription(this.getDescription())
.withViewDescription(HumanReadables.describe(view))
.withCause(new TimeoutException())
.build();
}
};
}
then you should only do:
@Test
fun shouldBeAbleToLoadList() {
onView(isRoot()).perform(waitDisplayed(R.id.recycler_view, 5000));
}
the 5000 is a timeout of 5 secs (5000 millis), you can change it if you want to.
After waitDisplayed
is executed it could happen that the element is shown or the timeout has been reached. In the last case an Exception
will be thrown.
回答2:
You will need to create an Idling Resource
for the binding.
You can check Android Architecture Components sample which have a similar implementation. Here's what you will need to look for:
- Firstly, you will need to add an
Idling Resource
class which checks if there are any bindings pending (you can find an implementation here) - Now you can create a rule which will automatically register/unregister
Idling Resource
for you (you can find an implementation here). - And now you can add this rule to your test and check if it works (sample test implementation you can find here).
来源:https://stackoverflow.com/questions/58482891/espresso-test-fails-due-to-data-binding