I'm writing UI tests in XCode 8 for a very complicated app. I'm discovering several issues, one of them being that each test I run takes several minutes.
The flow for the particular test I'm running isn't even that complicated.
I get many "Wait for app to idle" messages.
Does anyone know why the tests take so long to run, or do you have any suggestions on how I can speed them up? The WWDC demo was basically instant, but that was a very simple app.
Here's an example of what I see, and how long it takes. (The test is also still running!)
t = 504.16s Wait for app to idle
t = 625.29s App animations complete notification not received, will attempt to continue.
t = 625.30s Synthesize event
t = 625.67s Wait for app to idle
Thanks!
Update: When I slightly interact with the app, while keeping the elements I need visible, like by pulling on a tableview, the tests continue.
Thanks #Oletha - I found the issue, which may be specific to this app:
After deleting large amounts of code at a time to see where the issue was, we found that we were calling beginRefreshing and endRefreshing on a subclass of UIRefreshControl, on consecutive lines.
Something about that stalled the UI, enough to hold the app in "Wait for app to idle" for minutes.
We replaced those two lines with a custom method, and now tests are running normally.
The framework will wait for the app to become static, so if any views are animating or any activity indicators are shown, possibly including the network activity indicator in the status bar, the framework will hang until the app is still. This is to prevent race conditions with animations and components loading while the UI tests run.
If you think it's OK to override some of this behaviour to speed up your UI tests, you can try experimenting with removing activity indicators.
I encountered the same issue. The solution was to move the call to endRefreshing from the handler to the end of a network method triggered in the handler. In other words, if you refresh a table view, make sure to endRefreshing after all data has been loaded.
In my case was because I had some animations (UIView.animate...) so I had to UIView.setAnimationsEnabled(false) when running the app from the UI tests (you can send launchArguments or something).
Set launchArguments just before calling app.launch():
app.launchArguments = ["enable-testing"]
app.launch()
Then in the AppDelegate, check launch arguments:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
#if DEBUG
if CommandLine.arguments.contains("enable-testing") {
UIView.setAnimationsEnabled(false)
}
#endif
// ...
}
Thats all!
I had the same issue - on an actual device I would continually see "Wait for app to idle"
2 simple solutions:
1) If you're running on a device, make sure it's kept still (i.e not moving). Running tests while holding the device it would always be waiting for the app to idle. Placed flat on a desk, tests ran fine.
2) Alternatively, run your tests on the simulator if possible
Related
I would like to ask a question regarding UI Test when continuously navigation from one page to another and so on. If I launch a UI Test, do I need to continuously navigate from one page to another for one specific test or can it be chopped into pieces without launching the app again and again? Thanks
e.g.
[Test]
public void Navigation()
{
app.Tap("button1NavigateToPage1");
app.WaitForElement("ElementInPage1"); //If Success
app.Tap("buttonNavigateToPage2");
app.Tap("buttonNavigateToPage3");
}
The test should start from normal state. That is why the app is relaunched every test.
It shouldn't take a lot of time and you don't have to watch it doing the things it does.
It will show you a summary in the end of failed tests and you can rerun those and check them again.
You can also take a screenshot to make sure that the flow went as intended
app.Screenshot("First screen.");
I'm having a problem where when I click on submit in a dialog box the resolve event on the dialog box is supposed to call a function. The dialog closes when the button is click but the api function is never called (as seen from dev tools). If I stop the tests and manually do the action in the cypress browser it will work call the function. I'm not sure why the manual click is acting different than the test runners click?
Sometimes you need a hard ms wait(should be avoided)...
You may have a wait issue. Add a cy.wait(1000) 1000ms hard wait and see if that fixes it. If it does, you will want to identify what hasn't loaded fully when the button is pressed. While hard waits should be strongly avoided, I find myself using them sparingly with the smallest amount of milliseconds(x2 for safety) that I can get by with. Animations are one such example that comes to mind.
Make sure all subscriptions are ready
I am working on a meteor app that using websockets(tricker to wait on than xhr events). I call a method that returns true/false for all subscriptions. This helps wait for any events that cypress doesn't see in the network layer. Ask your devs if there is a method in your application that returns true/false depending on the ready state of the application.
Review for Race condition
This could be a legitimate race condition. I would review it with the devs just in case.
I have a legacy carbon app that used MPRemoteCall to show an assert dialog when something bad happens on the main thread of the app. Sometimes an assert happens in a worker thread that shouldn't be doing UI stuff. However it's deprecated a long time ago and I was wondering what the replacement API is?
I think - [NSObject performSelectorOnMainThread:withObject:waitUntilDone:] is probably the closest analog to MPRemoteCall().
GCD/blocks should also work:
dispatch_sync(dispatch_get_main_queue(), ^{
// do work here
});
Hopefully these will work correctly if your app is still using a Carbon event loop--I have had problems in the past with Cocoa timer functions in Carbon apps.
I'm making a app with using XF PCL.
I started to doubt that XF's timer is not good enough for UI routine.
for example, in iOS native app, If View is not showing some reason (new page's pushed or something), scheduler is stopped automatically. Because it's for UI.
But Xamarin's doesn't seem like that.
It's still doing his job.
I hope at least within "Device.BeginInvokeOnMainThread(() =>" will be skipped when it's disappeared.
Am I right?
Should I put extra logic for that?
(like stop and restart timer?)
(or declare variable to skip in it?)
Thanks.
In Andriod usually when you are in Page1 and then you navigated to Page2, although Page1 is hidden it is not killed therefore the worker threads would still working (in that case it is the timer)
My suggestion is to override the OnDisappearing method in the code behind of Page1 and place the code that would make the timer stop or being ignored.
If it is for me , I would also place a boolean flag and called _isPageShown so when you override OnDisappearing and OnAppearing you just put the value of _isPageShown to false or true accordingly. Then in the callback of the timer you check the flag if it is true or false and act accordingly.
I'm writing a Cocoa app, and I need to indicate to the user that a view is invalid and can't respond until a background job is completed.
Longer version:
A view in my app contains some data fetched from a server. When the app determines that the data is invalid, it fetches fresh information and displays it. Usually the update happens too quickly to see, but if server or network problems cause the background job to take more than a second or two, I need to make sure the user
Cannot interact with the view until the job is finished;
Can tell that the view is awaiting an update; and
Can continue to interact with other parts of the application until the job is finished.
1) is important because any operation initiated with the stale data will fail, and if the network or the server is slow, I don't want the user to waste his time with futile attempts to interact with that part of the application. (It's fine if the user can still see the data. That could conceivably be helpful, actually, but it isn't crucial.)
What is the standard way to do this in Cocoa? For what it's worth, in the Eclipse framework I think I used the showBusy method on ViewPart.
If there isn't a standard way, does anyone have any suggestions for tackling it? I haven't been using OS X very long, so I'm not even sure how an app is supposed to look and behave in this situtaion. (The screenshots on this page looking promising, but it's iOS, not OS X: http://cocoawithlove.com/2009/04/showing-message-over-iphone-keyboard.html)
Right now I'm working on replacing the whole view with an NSProgressIndicator and restoring the view after the task finishes, but removing the view means my app could end up in a funky state if something goes wrong. Also, I'd like to be able to factor this functionality out into its own class or category(?) since I will have this issue with several different view classes in my application. Ideally I'd like to end up with something as simple as Eclipse's view.showBusy(true)/view.showBusy(false).
Any and all help would be appreciated.
Thanks!
Why not just draw another view on top of the busy view? You could either have it display an image that means busy, or draw it as semitransparent, so it shades out the before-update version of the busy view.