Before I submitted this to Apple as a bug report, I wanted to double check I am not doing something very silly.
I have attached a sample project that demonstrates the problem. I have two segmented controls, one of which controls the enabled state of the other.
https://www.dropbox.com/s/dq2x9srbme4genb/EnabledControlsProblem.zip?dl=0
If you click on the disabled button, it disabled the second segmented control.
I have a basic UI test that replicates this behaviour which is constructed like this:
XCTAssertTrue(app.segmentedControls.buttons["Enabled"].exists)
XCTAssertTrue(app.segmentedControls.buttons["Enabled"].isSelected)
XCTAssertTrue(app.segmentedControls.buttons["First"].exists)
XCTAssertTrue(app.segmentedControls.buttons["First"].isSelected)
XCTAssertFalse(app.segmentedControls.buttons["Second"].isSelected)
XCTAssertTrue(app.segmentedControls.containing(.button, identifier: "First").firstMatch.isEnabled)
app.segmentedControls.buttons["Disabled"].tap()
XCTAssertFalse(app.segmentedControls.containing(.button, identifier: "First").firstMatch.isEnabled)`
If you run this test on an iOS 11.4 device it works as I think it should and succeeds. However, if I switch to a iOS 12.1 simulator device, the test fails on the last line (which is checking if the second control is enabled). Visually state of the control changes as it should. I have also tried this same test on an actual iPhone device running 12.0
Any attempts to sleep before the last check, don't make a difference.
Is there something fundamentally wrong with what I have done or is this a bug or a change in iOS12 that I missed?
I used to have the same type of problems with UI testing and I discovered a better method in pretty much all cases, and this lies in the key point that all tests rely on the Accessibility Framework. I was getting these weird seemingly buggy results all the time.
1. Better Debugging
Debugging goes much easier when you use the following command, which will show the hierarchy of elements:
print("Current App Tree: \(app.debugDescription)")
The hierarchy will help you ensure that the element does exists and how the app/device/simulator is seeing it.
2. Use Accessibility Identifiers
Instead of finding elements as you are, understand that all the tests are built on the accessibility framework. This seems insignificant at first, but I found it made life much easier. The framework is much happier when all elements have an accessibility identifier. Basically each view has a property: "accessibilityIdentifier". This will show up in the app debug listed above. If you do not set this, the framework will have a hard time finding your elements. If you're using storyboards, this is easy to set under Accessibility. If you're using code, set the property as follows:
myView.accessibilityIdentifier = "Enable State Selection"
Then in your UI Tests you can find the element like this:
app.buttons.matching(identifier: "Enable State Selection").element
In your specific case, it looks like it is part of a UI group of some kind involved in segmented controls. I would make a two step finding like this:
myUIGroup.accessibilityIdentifier = "Controls"
Then you can do a two step finding like this:
let controlsView = app.segmentedControls.matching(identifier: "Controls").element
let enabledButtons = controlsView.buttons.matching(identifier: "Enable State Selection").element
XCTAssert(enabledButtons.exists)
Overall I find that you at the beginning of your UI Testing class define these as class globals:
let app = XCUIApplication()
lazy var controlsView = app.segmentedControls.matching(identifier: "Controls").element
lazy var enabledButtons = controlsView.buttons.matching(identifier: "Enable State Selection").element
Then in your test function the code is very clean, and simply:
XCTAssert(enabledButtons.exists)
Related
With the rise of different apps, which hijack the iOS simulator (such as flawless or now sherlock) I'm curious how this is done.
As far as I've found there were options based on SIMBL but I don't think the above use it. Further more there is Injection (https://github.com/johnno1962/injectionforxcode) which uses XPC's to inject code.
I would guess there are different ways (e.g. how is the UI change done? both either the flawless layer on top of it, or the variable change in sherlock) so it would be great if someone here would point me in the right direction.
I havent used the plugins BUT:
sherlock loads code in the app being run BY the simulator from what I read. (https://www.hackingwithswift.com/articles/131/sherlock-turbocharges-your-ios-simulator)
flawless but afaics on the website all it does is overlay a UIWindow ontop of the simulator window. Even a separate app could do this.
(https://flawlessapp.io/)
SIMBL could do both id I'd say...
but you can do a lot by attaching LLDB too! really powerful. (a good example is https://github.com/facebook/chisel)
xcode's code injection is basically the same (roughly ;)
=> there is still no public / or private api for the simulator AFAIK
=> targeting apps inside simulator seems easier to me
(there are other ways by manipulating defaults or by playing around with simctl process)
I'm working in Xcode 9 on a Swift project, and I'm in table flipping mode with this.
Here's my end goal: put my tests next to my logic in a playground so that as I write the logic, I can see the tests pass in close to real time.
I will also accept how to put my tests in a playground by themselves, I just don't want to be running tests on a simulator for every little thing.
My problems in trying to get this to work are many. First and foremost, I can't find a way to add a playground directly to my project/workspace. When I right click on a group in the project navigator and select 'New file...' I get this:
As you can see, no option for playground. Filtering also returns no results. If I press the plus button at the bottom of the project navigator, the same dialog (with no playground option) pops up.
I thought it's in another menu so I went to the File menu, and in "New >" I find an option for creating a playground. I select a blank playground from the chooser, choose where to save it (strange that it wanted to save it under 'Unsaved Xcode Documents') and a basic playground comes up in a new tab. Odd thing I notice, though, is that there's no longer a play button or scheme/simulator selector at the top on this tab.
It now doesn't recognize ANY modules other than Apple provided ones! And sure enough, creating the playground didn't actually add it to my workspace. I guess I have to manually add it to my project. Upon doing so, it will recognize my pods but none of my project files! Any class I try to use gives me an Unrecognized identifier. Trying to import my project doesn't work either, it just gives me a No module.
I tried clearing DerivedData, I tried cleaning, I tried restarting Xcode, I even made sure I had an iPad Pro in the list of simulators (since that seems to matter for some people).
In frustration, I deleted the playground, and resolved just to deal with running my tests constantly. But now, my project has persistent errors! Despite moving the playground to the trash, Swift continues to error saying 'Unresolved identifier' for that playground!
As the excellent Brandon Williams says in his talk at FrenchKit, a playground "does not get access to anything in the application target." Which is ridiculous, but hey, what are you gonna do. So the answer is: there is currently no way to do this the way I asked.
It IS possible, however, to achieve this functionality if your logic is in its own framework/cocoapod. That is, move the logic you want to test out of your app target and into an outside library that your app, subsequently, imports. This will allow your playground to import it as well, and you can test to your heart's content.
I try to add UI test cases to our existing mac application. I already tried UI test cases for iOS, which includes the following steps
I choosed Appium instead of Apple's UIAutomator, because I want to maintain the same flow for our Android apps too.
To proceed the automation, I need to set Labels/Values/Identifiers to the elements. So that they can be accessible. We used drawRect method in most parts, so I followed this post to make drawed components accessible.
I can set/read elements in iOS. What I did is, whenever I draw an element, I simply created an UIAccessibilityElement and added in the corresponding view.
Now, I'm trying to write UI tests for our mac application. As Appium does not have support to the mac application, I considered to use Apple's XCTest UI recording/playback to automate my mac application.
Here is the steps that I took:
NSAccessibility is the class that provides accessibility to the mac application
With XCTest, If I knew a particular elements (say a button) identifier/label, I can proceed with automated tap action
Unlike UIAccessibility, NSAccessibility is a role based object. That is, we need to mention which type of accessibility element that we are going to define.
To start the automation process, I took my mac applications left panel, which has five buttons aligned vertically in it
I set identifiers to those buttons. Then I opened Accessibility Inspector and opened my mac app. The values are properly set.
Then I tried to use record option in XCTest. When tapping the button, crashes the app with the error
"Recorder Service Error: Left Mouse Down: Failed to find matching
element".
I posted about it here.
Questions:
Can someone suggest me the right path to automate mac application? Am I going in the right way?
I googled a lot to see a working sample code about how to implement NSAccessibility. But I can not found anything. Can someone share any useful links/samples?
All I need is, to get elements by identifier/label. Accessibility Inspector shows the right value where as XCTest can not read the identifiers. Did anyone face this issue?
Thanks in Advance
So I downloaded the beta of XCode 7 and I've created some UI tests, but I can't find the functionality of how to take screenshots of my app/UI during the test.
Can someone please help?
UI Testing in Xcode automatically takes screenshots of your app after every step.
Simply go to one of the tests that has already ran (Report Navigator > choose a Test), then start expanding your tests. When you hover your mouse over the steps, you will see eye icons near each step that has a screenshot.
Here's an example... in this case, notice the eye icon at the end of the gray row. If I were to tap on it, I would see a screenshot of the app right when the button in my app was tapped (since the step is Tap the "Button" Button).
If you want to generate screenshots, you can also use snapshot, which describes how to trigger screenshots in UI tests: https://github.com/fastlane/fastlane/tree/master/snapshot#how-does-it-work
It basically rotates the device to .Unknown (Source), which triggers a snapshot without actually modifying your app's state.
Comparing the output with the generated plist file enables you to even properly name the screenshot
Facebook's ios-snapshot-test-case and KIF both run as Unit Tests, and therefore are in the same process as the app. As such, they can directly access views and use something like renderView: or snapshotViewAfterScreenUpdates. Xcode UI Testing runs in a separate process, and therefore cannot directly access the views.
UI Automation, Apple's now-deprecated Javascript UI testing library, had a function calledcaptureScreenWithName.
Unfortunately, the new Xcode UI Testing lacks any similar function in its testing library, which to me seems like a glaring omission and I encourage you to submit a Radar for it, as taking screenshots is fundamental to perceptual difference tests (which it sounds like you're trying to do). I hope (and expect) that able will address this deficiency in later Xcode updates.
In the meantime, there are more creative approaches to taking screenshots. See this stack overflow response for a workaround involving taking the screenshot in the app itself and then sending it to the test process.
I' ve created a tool that saves the tests last n screenshots and generates the JUnit tests results report, parsing TestSummaries plist file from test logs. https://github.com/nacuteodor/ProcessTestSummaries
Maybe, that helps you.
Facebook's FBSnapshotTestCase can be alternative solution:
https://github.com/facebook/ios-snapshot-test-case
I found several other threads with similar problems, but no one has exactly the same problems.
Besides it DID work some time! the errors now keep occurring while it was working some time before..
When Running my app, that has a build Target "Today View Extension", I get no actual result.
The Extension is shown in Notification Center, but has no body (Simulator AND device).
Also when I try to run the App (not the target extension) and attach the process manually by PID I get this error:
I also had the error that my Extension (that has a "Bundle Display Name" entry in Info.plist for a custom Name) did show the Name of the Extension-containing App, and not the string that was set in the Info.plist
Strange thing is that sometimes it worked, sometimes it doesn't, but when it does not there were like five different reasons why not.
I want to ask people who have similar/same problems to post them here to collect all the issues appearing and possibly collect workarounds / solutions for these problems.
Thank you.
For anyone having troubles now:
With beta 4 and beta SDK 4 a lot of bugs were fixed:
[self setPrefferedContentSize:]
to set the views size is now working properly (if you have troubles viewing your Extension)
If your updated Extension is not showing in Notification Center be sure to have a look at the log output, there you can see what task the debugger is attached to, if there is "no Selection" try stopping and running again, it will work after some tries!
If you have questions feel free to ask,
Happy coding
I don't exactly have a solution, but I've observed this happening when anything is "wrong" with my Today extension. For example, if I don't have a file properly targeting the widget. I'm guessing that instead of just crashing to the home screen, iOS just gives you an empty widget? I've written about my own issues here, for reference.
I had similar issues. But it seems to be alright now. Since the "Today View" is an extension and is bundled with the containing app, you should just build and run the containing app. From there, you can pull down the "Today View" and if your widget / today view is not added, add it.
You should be able to see all your updated changes without a problem with this and you won't have to attach any process.