I have an activity that fires an asynchronous request to a server.
Right when it is fired, I show a progress bar. When a result is returned to the activity, I set the progressbar to View.GONE.
I have two tests right now.
One for testing trying to login with wrong credentials
which works perfectly :
#Test
public void tryLogin__withWrongPassword() {
onView(withId(R.id.loginEmail))
.perform(typeText("em#ail.com"), closeSoftKeyboard());
onView(withId(R.id.loginPassword))
.perform(typeText("123456789"), closeSoftKeyboard());
onView(withId(R.id.loginSubmit))
.perform(click());
onView(withId(R.id.loginProgressBar))
.check(matches(isDisplayed()));
onView(isRoot()).perform(waitFor(3000));
onView(withId(R.id.loginProgressBar))
.check(matches(not((isDisplayed()))));
onView(withId(R.id.loginPassword))
.check(matches(hasErrorText("Wrong password")));
}
And one that I test with no internet connectivity that does not work :
#Test
public void tryLogin__withoutInternet() {
onView(withId(R.id.loginEmail))
.perform(typeText("em#ail.com"), closeSoftKeyboard());
onView(withId(R.id.loginPassword))
.perform(typeText("123456789"), closeSoftKeyboard());
onView(withId(R.id.loginSubmit))
.perform(click());
onView(withId(R.id.loginProgressBar))
.check(matches(isDisplayed()));
onView(isRoot()).perform(waitFor(3000));
onView(withId(R.id.loginProgressBar))
.check(matches(not((isDisplayed()))));
onView(withText("Network error, try again later"))
.inRoot(withDecorView(not(mActivityRule.getActivity().getWindow().getDecorView())))
.check(matches(isDisplayed()));
}
The test fails because the progressbar in this case is shown for a fraction of a second (because the error is almost immediately dispatched from the viewmodel)
and seemingly Espresso cannot catch it while it is still loading.
My question is how could this potentially be fixed in my test? I have read somewhere that Espresso cannot test progress bars and the advice was to use UIAutomator instead, however I just started with Instrumented tests and chose Espresso and it is difficult for me to find the ideal tool for this case. Moreover, it seems to be working just fine when the progressbar appears for a bit more than half a second (in my first test for example)
P.S. the waitFor(long millis) method is a utility add-on method I made to force Espresso to wait for a specified amount of time before checking something (enforcing a timeout as a quality requirement)
Edit for clarification :
My main question here is if anyone has an idea why the visibility is not caught when it is active for less than an amount of time vs when it lasts for more than half a second, even if the check is done immediately after the perform(click()) call.
Related
I'm unable to find an element (UiObject2) using UiAutomator within my androidTest. I obtained UiDevice instance and try to find the object with this:
MY_UI_DEVICE.findObject(By.res(CURRENT_PACKAGE, id));
CURRENT_PACKAGE is the package of my app MY_UI_DEVICE.getCurrentPackageName(). I tried also with this one:
MY_UI_DEVICE.wait(Until.findObject(By.res(CURRENT_PACKAGE, id)), 10000);
I can see the app is waiting for 10 seconds on the right screen (where the desired object persists), but after timeout it fails to find it and test fails. It always fails on emulator (API 23), but rarely works good on a real device (API 25).
When I debug the code I could see that manually I could obtain the right element by calling sequence of getChild(index) methods on AccessibilityNodeInfo but in the runtime it still fails even the app is waiting on the right screen where I expect the specific element.
I was playing with different UiDevice's functions, but none of the helped and I'm out of ideas, so any help will be appreciated.
There were 2 issues with my tests:
The first problem was in getting / initialising UiDevice instance in static block (as a static field in util class). I moved it into #Beforeand it helped to resolve the issue partially.
Another problem was occurring while searching for an element using a package name obtained from the UiDevice. I replaced getting package with InstrumentationRegistry.getTargetContext().getPackageName(); as it's done in google samples.
Make sure that your Test method is throwing the UiObjectNotFoundException. I had this issue with UiObject2 as well until I started forcing the error throw
#Test
public void clockTest() throws UiObjectNotFoundException, InterruptedException {
mDevice.click(1146,37); //click on clock top right corner
Thread.sleep(1500);//wait 1.5 seconds for screen to load
mDevice.click(1138,135);//clicks in shell
Thread.sleep(1500);//wait 1.5s for screen to load
UiObject2 dTSettingsButton = mDevice.findObject(By.text("Date & Time Settings"));
//assertNotNull(dTSettingsButton);//find and assert the settings button
dTSettingsButton.clickAndWait(Until.newWindow(), LAUNCH_TIMEOUT);//clicks the settings button
UiObject2 timeFormatButton = mDevice.findObject(By.text("Select Time Format"));
assertNotNull(timeFormatButton);//find and assert timeformat button
timeFormatButton.clickAndWait(Until.newWindow(), LAUNCH_TIMEOUT);//click timeformat button
UiObject2 twelveHourButton = mDevice.findObject(By.res("com.REDACTED.settings:id/first_btn"));
assertNotNull(twelveHourButton);//find and assert twelvehour button
twelveHourButton.clickAndWait(Until.newWindow(), LAUNCH_TIMEOUT);//click twelvehour button
}
Try use UiSelector methods. That worked for me much better than By selectors
Tried the login use case with appium for android native app. But button click not working. But I am getting all test passed.Tried with mobile driver also.
#BeforeClass
public static void setUp() throws MalformedURLException
{
DesiredCapabilities capabilities=new DesiredCapabilities();
capabilities.setCapability("BROWSER_NAME","Chrome");
capabilities.setCapability("VERSION","4.3");
capabilities.setCapability("deviceName","SGH-T999L");
capabilities.setCapability("platformName","Android");
capabilities.setCapability("appPackage","org.odk.collect.android");
capabilities.setCapability("appActivity","com.fieldforce.android.activities.LoginActivity");
webDriver=new RemoteWebDriver(new URL("http://127.0.0.1:4723/wd/hub"),capabilities);
// webDriver.manage().timeouts().implicitlyWait(80, TimeUnit.SECONDS);
}
#Test
public void testLogin() throws Exception
{
// webDriver.switchTo().window("NATIVE_APP");
WebDriverWait wait = new WebDriverWait(webDriver, 10);
WebElement userName= webDriver.findElement(By.id("txt_username"));
userName.sendKeys("733894");
WebElement password= webDriver.findElement(By.id("txt_password"));
password.sendKeys("Pass#123");
WebElement login_button= webDriver.findElement(By.id("org.odk.collect.android:id/btn_login"));
wait.until(ExpectedConditions.visibilityOf(login_button));
login_button.click();
}
#AfterClass
public static void tearDown()
{
webDriver.quit();
}
Of course the test will pass, as you are just trying to click on Login Button, it doesn't matter for your test case whether it should pass or fail unless you add some Assert after clicking on login button.
Try adding some wait after click on login
driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
And then try asserting something in #test for method testLogin like add below two statement in the end, and make them compatible with locator you get after login
Actualtext = driver.findElement(By.xpath("locator to verify after login")).getText();
Assert.assertEquals(Actualtext, "assert Text");
I got the same issue.
Please make sure your submit button(UI) is not covered by the phone keyboard.
If it covers, Solution :
hide the keyboard before run the click [.click()] function.
keyboard hide code is here How to dismiss the keyboard in appium using Java?
Reason:
Coz Appium clicks the button using x&y UI coordinates and if the keyboard covers the submit button it clicks on the keyboard not on the button.
In my case I've also tried similar button clicking with no result. I've realized that I wasn't clicking the right element.
To verify what I was clicking I've printed element's Text and TagName:
List<MobileElement> views = driver.findElements(By.className ("android.widget.TextView"));
System.out.println("1: "+views.get(2).getText());
System.out.println("2: "+views.get(2).getTagName());
views.get(2).click();
Output was:
1: OK
2: android.widget.TextView
My classNames I got through UI Automator Viewer, Android/Sdk/tools/bin/uiautomatorviewer application.
Although, before list's getting you could temporarily use Thread.sleep(5000); with your own values to check assumption about slow element loading and replace it with proper time waiting method if required in case of time problem.
Sometimes a button could have multilayer structure and it is required to get the right layer you could click. In my case there were two layers.
Would suggest using
WebElement login_button= webDriver.findElement(By.id("btn_login")); //as used for other WebElement
Also i believe a general login page shall ideally be having the login visible, if that not be the case, you might want to perform a scroll down on the page instead of
wait.until(ExpectedConditions.visibilityOf(login_button));
PS - If you are receiving any error/exception post this. Please add to the question and let know.
As your keyboard hiding the Login button, so hide the keyboard by,
driver.hidekeyboard();
While writing a Robolectric unit test, I noticed my getVisibility() call returned 0 (VISIBLE) after calling fab.hide(), so I assumed it was due to animation and to test it out, added a delayed check. Surprisingly it has also returned VISIBLE. On the actual device it works as expected and returns the correct values.
EDIT: just to clarify I'm using the FAB from the design support library.
My test code is really simple:
fab.performClick();
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
assertThat(fab.isShown()).isFalse();
Code under test:
mActionBunnot.hide();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
boolean shown = mActionBunnot.isShown();
Log.d(TAG,""+shown);
}
},2000);
When run through Robolectric, both here and in the test, isShown returns true
You can also .isShown() method to get the visibility.
I know it is a bit late, but maybe useful for other people.
Robolectric executes all operations on one single thread. In the past this happened synchronously. Since version 4.3 Robolectric has the Looper PAUSED mode, which improves this behaviour as described in this blog: http://robolectric.org/blog/2019/06/04/paused-looper/
On Android, when I touch the screen during a long function (required time > 30 sec) 15sec after the click, a message "Application isn't responding" appears.
I don't want to see it.
I did a test project, with 2 buttons and a function "LongProcess" for simulate a long process (it's just a sleep of 30 sec). My first button "LaunchFunction" just call the function. My second button "LaunchThread", launch a Thread who will execute my "LongProcess".
In the first case I have my problem but in the second case it works perfectly (the message will never appears because my main form is not waiting).
However, I have to wait the end of "LongProcess" (therefore the end of the Thread) because I have to do others things after it who need the result of the "LongProcess". So I tried to wait my Thread with many methods. I tried with the "WaitFor" of TThread Class but it repeat the initial problem. I tried also with a simple "while".
while not fThread.Finished do
begin
Sleep(500);
end;
But it's the same, if i touch the screen the popup will appears again.
Critical Section instead of the "while" or the "Thread.WaitFor" did exactly the same.
So I tried to update the GUI in my "while" for show to Android that the application is working.
while not fThread.Finished do
begin
Sleep(100);
Label_Test.Text := 'Msg' + IntToStr(i);
Inc(i);
Application.ProcessMessages;
end;
I see my label value change, if I touch the screen nothings change. 15sec later I will have the popup (but I will still see my label be updated on the background).
Someone have an idea ? Maybe can we disable event during a long process (so the click will not be in the queue so he should not be considered like "not responding" after 15 sec). Application.ProcessMessage don't seems works about that on Android.
Or maybe something in the Android API exists for say to the OS we are not inactive ?
For finish, if I click on "Wait" the application will work perfectly. If I don't touch the screen also (w/o thread too, until I don't touch the screen), but I see so many user click on "OK" like a robot (this close the application ofc...). Ty for your futur help
ps : I tried to replace the thread by a timer because I saw it on a forum, but it changed nothings.
ps2 : Here a .zip or the demo project http://www.partage-facile.com/YOJT1A8CLE/testproject.rar.html
If you block the main thread for too long, you will get an ANR. There's no way around it. Don't try to make your app wait. Just initiate your "other things" at the end of the LongProcess in the separate thread.
What you can try is use a Timer to operate it, but that will just be a very stupid thing to do, Android is overall slow, but this is your mistake. There really isnt a way around it with the way you are trying. You can try and declare an OnTerminate event for the thread to notify the main thread that the work is completed or find a different way without Sleep()
I'm using robotium 3.1 and I'd like to wait for a view to disappear, is there some way I can do that easily? My current way involves a ugly busy-loop with sleeps that makes no one happy.
To clarify what I'd like to happen:
waitForView(<View>) //The view appears
//The view is visible for a few seconds
waitForViewNotThere(<View>) //waits until the view has disappeared
The view that appears doesn't contain any text or such either. Any input is very much appreciated.
This is how:
final TextView helloWorldText = solo.getText("Hello world!");
solo.waitForCondition(new Condition() {
#Override
public boolean isSatisfied() {
return helloWorldText.getVisibility() == View.INVISIBLE;
}
}, 10000);
Whatever you do you are probably going to have some sort of sleep in the loop. (If you look at robotiums source it also uses sleeps). You can keep them to a minimum by using the waitforidlesync method on instrumentation that waits for the Ui thread to become idle.
if you want to wait for a view to disappear, use solo.waitForDialogToClose(long timeout).
Parameters :
timeout - the amount of time in milliseconds to wait.
returns : true if the Dialog is closed before the timeout and false if it is not closed.