Assume view state with Espresso test - android

JUnit library has an Assume.* instructions like Assume.assumeTrue(boolean) which works like assertions, but not cause test to fail and just to been ignored.
I want to perform such checking in arrange part of test for one of my views, by example assume, that founded checkbox is checked before starting the act part of test.
Take a look:
#Rule
public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
#Test
public void deselectFilter_AllFiltersSelected_CheckboxAllSelectedUnchecked() {
//arrange
ViewInteraction checkBox = onView(
allOf(withId(R.id.cbCheckAll), isDisplayed()));
//assume that this checkbox is checked
//act
...
//assert
...
}
In the arrange part i've received not a View, but ViewInteraction.
So I can perform such assertion like checkBox.check(matches(isChecked()))
But how to perform assume?

You could write a custom ViewAssertion to assume that no Exception is thrown when Espresso ViewMatcher fails:
public static ViewAssertion assume(final Matcher<? super View> viewMatcher) {
return new ViewAssertion() {
#Override
public void check(final View view, final NoMatchingViewException noViewFoundException) {
try {
ViewAssertions.matches(viewMatcher).check(view, noViewFoundException);
} catch (Throwable e) {
// Assume that there is no exception
Assume.assumeNoException(e);
}
}
};
}
Then you can use that assertion to assume like:
onView(withId(R.id.cbCheckAll)).check(assume(isChecked()));

The only way i've founded at this moment is just finding assuming view manually with activity from test rule. And then assume via jUnit.
CheckBox checkBox = (CheckBox) mActivityTestRule.getActivity().findViewById(R.id.cbCheckAll);
Assume.assumeTrue(checkBox.isChecked());
If you know a better way, maybe with using Espresso, please answer. Seems that it impossible to access view directly from Espresso commands

Related

Disable auto-correct functionality in Espresso test

I wrote an Espresso test that writes some text to a TextView, performs an action and then checks whether the text in the TextView is still the same.
The test fails on one of the test devices (Huawei P20, Android 8.1.0) because the entered text is auto-corrected (from 1234 5678 to 12th 5678). And this fails my test. The text is not auto-corrected when I manually enter the same numbers.
This is how I input the text in my Espresso test:
onView(withId(R.id.reference_value))
.perform(scrollTo(), click())
.check(matches(isDisplayed()))
.perform(typeText("1234 5678"));
closeSoftKeyboard();
I know I could just change the input text to something that won't be auto-corrected. But I would like to have a solution that generally makes sure that the entered text is not modified to something else. Ideally without having to manually change the configuration of my test device.
Do any of you guys have an idea how I could accomplish this?
One way that works for me is to use replaceText() instead, although this still seems to be a bit of a hack.
Another option may be to disable autocorrect through some Android API call or manually through the UI like for animations.
This issue occurs with some keyboards. For me it was with Microsoft SwiftKey Keyboard was set as my default keyboard.
Two solutions that worked for me:
Solution 1: Change input type to TYPE_TEXT_FLAG_NO_SUGGESTIONS during test if using typeText which disables the suggestions & corrections for that view.
Example:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class HelloWorldEspressoTest {
#Rule
public ActivityScenarioRule<MainActivity> activityScenarioRule
= new ActivityScenarioRule<>(MainActivity.class);
#Before
public void setUp() {
// get the view & set the input type
activityScenarioRule.getScenario().onActivity(activity ->
((EditText) activity.findViewById(R.id.etHello))
.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS));
}
#Test
public void testText() {
onView(withId(R.id.etHello)).perform(typeText("Smoth go"));
onView((withId(R.id.etHello))).check(matches(withText("Smoth go")));
}
}
Solution 2: Using my own ViewAction
Helper.java -> This file is placed inside the same test package with test.
public class Helper {
public static ViewAction setTextInEt(final String value){
return new ViewAction() {
#Override
public Matcher<View> getConstraints() {
return allOf(isDisplayed(), isAssignableFrom(EditText.class));
}
#Override
public void perform(UiController uiController, View view) {
((EditText) view).setText(value);
}
#Override
public String getDescription() {
return "set text";
}
};
}
}
Test class:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class HelloWorldEspressoTest {
#Rule
public ActivityScenarioRule<MainActivity> activityScenarioRule
= new ActivityScenarioRule<>(MainActivity.class);
#Test
public void testText() {
onView(withId(R.id.etHello)).perform(Helper.setTextInEt("Smoth go"));
onView((withId(R.id.etHello))).check(matches(withText("Smoth go")));
}
}
So far solution 2 has worked very well for me. Before that smoth text was auto-corrected to smooth every time.
This can be done for the TextView as well just replace EditText to TextView in helper method.

How to perform #After for a specific JUnit test case?

I'm using Espresso to build UI tests. For some test cases I would like to call a specific after step to reset the state in case the script fails.
Is there a way to perform an #After step for a single JUnit test case (#Test)?
The only solution I can think of is to make a separate test class. But I would like the test cases to be grouped in the same test class.
It does sound a little odd ;) but ...
You could add a try/finally to the single test for which you want this after behaviour. For example:
#Test
public void testA() {
try {
// the body of testA
} finally {
// apply the 'after' behaviour for testA
}
}
Or, if you really want to use JUnit's #After then you could use the TestName Rule (since JUnit 4.7) as follows:
#Rule
public TestName testName = new TestName();
#After
public void conditionalAfter() {
if ("testB".equals(testName.getMethodName())) {
System.out.println("apply the 'after' behaviour for testB");
}
}
#Test
public void testA() {
}
#Test
public void testB() {
}

Set Spinner item with espresso

I want to know how to set an item in a spinner in espresso testing.
onView(withId(R.id.spinner_gender)).perform(click());
onData(allOf(is(instanceOf(String.class)))).atPosition(0).perform(click());
This code above does not work :/
Your code snippet looks correct, so there may be an issue with another part of your test class?
Are you getting an Exception or stack-trace you can update your question with? Also check the espresso documentation for a bit more explaination.
See small code example of how you can select a spinner option by text or it's position.
#RunWith(AndroidJUnit4.class)
public class BasicEspressoTest {
#Rule
public ActivityTestRule<MainActivity> testRule = new ActivityTestRule<>(MainActivity.class);
#Test
public void selectBySpinnerPosition() throws Exception {
onView(withId(R.id.spinner)).perform(click());
onData(allOf(is(instanceOf(String.class)))).atPosition(0).perform(click());
}
#Test
public void selectBySpinnerText() throws Exception {
onView(withId(R.id.spinner)).perform(click());
onData(allOf(is(instanceOf(String.class)), is("spinner's text"))).perform(click());
}
}

How to get found view from onView result(ViewInteration

for example here is my code:
#Test
public void manga_list2() throws Exception {
onView(withId(R.id.manga_list));
}
I try onView(withId(R.id.manga_list)) result is ViewInteration, I hope get finded View and I try onView(withId(R.id.manga_list)).viewFinder.getView() in android studio > watches tool, and i get error:
Executing a query on the view hierarchy outside of the main thread (on: Instr: android.support.test.runner.AndroidJUnitRunner)
how to get the view?
update
if it cannot, please recommend me other ui testing that can get view directly, the espresso is too black-box
"espresso is too black-box" - exactly the opposite. Below is the code you can use to get the whatever view from your activity. In the example I'm getting the ListView:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class EspressoTest {
#Rule
public ActivityTestRule<YourTestActivity> mActivityRule =
new ActivityTestRule<>(YourTestActivity.class);
#Test
public void doSomeStuff() {
ListView listView = (ListView)mActivityRule.getActivity().findViewById(R.id.manga_list);
}
}
More examples here - android-testing-templates.

Testing progress bar on Android with Espresso

The workflow should be the following:
Activity starts
Progress bar is visible
Network request fires (idling resource is already registered so espresso knows how to wait for it).
Progress bar is hidden
Text from network is shown.
Up to this point, I have written assertions for steps 1, 3, 5 and it works perfectly:
onView(withText("foo 1"))
.check(matches(isDisplayed()));
Problem is, I have no idea how to let espresso know to verify the visibility of progress bar before the request is made and after the request is made.
Consider the onCreate() method is the following:
super.onCreate(...);
setContentView(...);
showProgressBar(true);
apiClient.getStuff(new Callback() {
public void onSuccess() {
showProgressBar(false);
}
});
I have tried the following but it doesn't work:
// Activity is launched at this point.
activityRule.launchActivity(new Intent());
// Up to this point, the request has been fired and response was
// returned, so the progress bar is now GONE.
onView(withId(R.id.progress_bar))
.check(matches(isDisplayed()));
onView(withId(R.id.progress_bar))
.check(matches(not(isDisplayed())));
The reason this is happening is because, since the client is registered as an idling resource, espresso will wait until it is idle again before running the first onView(...progressbar...)... so I need a way to let espresso know to run that BEFORE going to idle.
EDIT: this doesn't work either:
idlingResource.registerIdleTransitionCallback(new IdlingResource.ResourceCallback() {
#Override
public void onTransitionToIdle() {
onView(withId(R.id.progress_bar))
.check(matches(isDisplayed()));
}
});
Espresso has problems with the animation. You can just set the drawable of the progress bar to something static just for the test and it works as expected.
Drawable notAnimatedDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.whatever);
((ProgressBar) getActivity().findViewById(R.id.progress_bar)).setIndeterminateDrawable(notAnimatedDrawable);
onView(withId(R.id.progress_bar)).check(matches(isDisplayed()));
As I can see Espresso is tightly coupled with skipping dynamic UI actions whatsoever, that's why you can't test ProgressBar using Espresso. However, you can easily accomplish this with another Android Google tool: UiAutomator as following:
saveButton().click(); // perform action opening ProgressBar with UiAutomator, not Espresso
assertTrue(progressBar().exists());
Using these static utils:
public static UiObject progressBar() {
return uiObjectWithText(R.string.my_progress);
}
public static UiObject saveButton() {
return uiObjectWithId(R.id.my_save_button);
}
public static UiObject uiObjectWithId(#IdRes int id) {
String resourceId = getTargetContext().getResources().getResourceName(id);
UiSelector selector = new UiSelector().resourceId(resourceId);
return UiDevice.getInstance(getInstrumentation()).findObject(selector);
}
public static UiObject uiObjectWithText(#StringRes int stringRes) {
UiSelector selector = new UiSelector().text(getTargetContext().getString(stringRes));
return UiDevice.getInstance(getInstrumentation()).findObject(selector);
}
Make sure your build.gradle includes:
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
It looks like this may not be truly possible. Though it is an older group posting, there is a fairly decisive answer in the Android Test Kit Discussion where it is stated that the UI threads don't rest during the animation of progress bars, and so the Espresso framework cannot execute.
Marcus Klepp recommends moving past this here through the use of build types. The Gradle plugin will permit you to define different build types. You could set up a different layout in your androidTest build type which replaces the View in question with something generic. If all you're doing is confirming that the widget isDisplayed() under one set of conditions, and not(isDisplayed()) under another set of conditions, then you could surely implement that through different layout files. Not that it is not a little bit of a lift.
Finally, there may be another post here which carries some additional information here: "java.lang.RuntimeException: Could not launch intent" for UI with indeterminate ProgressBar
In my case solution which was provided above works as well but I simplify it, so added build.gradle uiautomator library
androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3'
and created a new class which will work for example with Progress bar
public class ProgressBarHandler {
public static void waitUntilGoneProgressBar() {
progressBar().waitUntilGone(10000);
}
private static UiObject progressBar() {
return uiObjectWithId(R.id.progress_bar);
}
private static UiObject uiObjectWithId(#IdRes int id) {
String resourceId = getTargetContext().getResources().getResourceName(id);
UiSelector selector = new UiSelector().resourceId(resourceId);
return UiDevice.getInstance(getInstrumentation()).findObject(selector);
}
}
and in my tests use all Espresso methods and when needed only then address to UiAutomator in tests, for example
public class LoginTest extends AbstractTest {
#Rule
public ActivityTestRule<LoginActivity> createAccountActivityTestRule = new ActivityTestRule<>(LoginActivity.class);
#Test
public void loginTest() {
onView(withId(R.id.login_email)).perform(typeText("autotest666#gmail.com"));
onView(withId(R.id.input_password)).perform(typeText("Password123."));
onView(withId(R.id.login_log_in)).perform(click());
waitUntilGoneProgressBar();
onView(withId(R.id.fragment_home_title)).check(matches(isDisplayed()));
}

Categories

Resources