UI Testing with respect to a attribute value iOS/Swift - ios

I'm writing UI Tests for my application. I've an attribute named numberOfPhotos in PhotosViewController and it's value is set from another ViewController. Whenever numberOfPhotos is 2, only two photos should be displayed in my current PhotosViewController.
I was able to test if required number of images are visible or not but I'm not able to set numberOfPhotos from UITests class. Is there anyway I can set this value and proceed to tests.
Currents I'm editing the code to set value for numberOfPhotos and testing it. Is there anyway I can set this value from UITests class. Thanks.

They are different apps, that's why you can't set it directly. The UiTests use a test runner that runs your app. There is a way to do what you want using launchArguments.
From your tests:
let app = XCUIApplication()
app.launchArguments = XCUIApplication().launchArguments + ["UiTesting", "numberOfPhotos", "2"])
app.launch()
From your app:
var isUiTesting: Bool {
return ProcessInfo.processInfo.arguments.contains("UiTesting")
}
Write some code in your controller to check the isUiTesting property and set the number of photos based on the next few launchArguments. You will have to do some parsing of the launch arguments.

Related

How To Add Launch Arguments & Environment values on iOS Extension In UI Automation Test

I've got a UIAutomation test that tests an app with a Share Extension.
I want to pass in some launchArguments and launchEnvironment properties to the Share Extension in my test.
I can't figure out a way of doing it.
When debug the ProcessInfo in the extension my values aren't there.
I've tried setting them like this:
XCUIApplication(bundleIdentifier:"com.my.app").launchArguments = ["TEST"]
XCUIApplication(bundleIdentifier:"com.my.app").launchEnvironment = ["TEST":"TEST"]
and this:
XCUIApplication().launchArguments = ["TEST"]
XCUIApplication().launchEnvironment = ["TEST":"TEST"]
Note, I am correctly getting the correct process when I'm finding it with the bundle identifier.

iOS - KeyValueStore not working across devices

I have an app that will implement key value store in order to save some variables and sync across devices.
I have a view controller with the following:
var keyStore: NSUbiquitousKeyValueStore?
On viewDidLoad I do
keyStore = NSUbiquitousKeyValueStore()
Then when a user finishes watching a video I run
keyStore?.set(num, forKey: "watched") // where num is a number
keyStore?.synchronize()
In order to check the value of the "watched" variable, I'm running this on didFinishLaunchingWithOptions:
keyStore = NSUbiquitousKeyValueStore()
let val = keyStore?.string(forKey: "watched")
print(val)
If I test the app several times, the value of val changes accordingly. However, when I test on a different device, val returns nul. If I use the application in this new device, the value for "watched" changes but values never sync between the two devices.
I read that there is a Notification Method to check if the stored value changed in real time, but in this case I'm reading the stored value during app launch. Wouldn't that make it unneeded?
My bad. Make sure you are logged into Cloud in your simulator.

Can not access the programatically added UIView in XCTest

Below is my recorded XCTest
let app = XCUIApplication()
let tablesQuery = app.tables
tablesQuery.staticTexts["Video"].tap()
tablesQuery.staticTexts["\tWindowed"].tap()
app.buttons["Launch"].tap()
app.buttons["Popout Video"].tap()
app.children(matching: .window).element(boundBy: 0).children(matching: .other).element(boundBy: 1).tap()
When I am trying to run the test the last part that is:
app.children(matching: .window).element(boundBy: 0).children(matching: .other).element(boundBy: 1).tap()
is not accessible. It does not throw any error but the last line of code is not executed.
I have tried solving the issue by referring to the following stackoverflow question :
Xcode UI Tests can't find views that are added programatically
Also , I have referred to the following Apple Documentations: https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/iPhoneAccessibility/Making_Application_Accessible/Making_Application_Accessible.html#//apple_ref/doc/uid/TP40008785-CH102-SW2
https://developer.apple.com/reference/uikit/uiview
But all these doesn't seem to solve the issue. Any help will be greatly appreciated.
The line in question is very brittle. It's possible that your app's views are not always available in the same order every time the app launches, depending on race conditions, so what was recorded in your recording session does not necessarily work for all launches of the app. You'll probably find that the code is actually running, but isn't tapping the element you expected.
To make your code less brittle, add an accessibility identifier to the UIView in your app code, and use the otherElements query to find the UIView.
// app code
let view: UIView!
view.accessibilityIdentifier = "myAccessibilityIdentifier"
// UI test code
app.otherElements["myAccessibilityIdentifier"].tap()

Is there any way to check element exists in XCTest other than .exists function?

Currently, I am working in XCTest iOS framework and using .exists function to check the element presence. Want to know if there is any other way to check the presence of element on UI as .exists is getting problem. Tests get successful on the first run but when it runs second time, it gets failed because script clicks the element which is not exist on the UI might be because elements loaded first time in app remains hidden but exists.
Please let me know any function which checks the current screen elements presence.
I too am looking for a way to wait until an element appears instead of using the sleep method. sometimes UI elements take longer to load, but that doesn't mean it won't appear, eventually. At which point you can check use the .exists
There is a XCUIApplication Class Reference, not hosted by apple, but its in OBJ-C:
http://masilotti.com/xctest-documentation/index.html
UPDATE: I think I found a solution from this link Delay/Wait in a test case of Xcode UI testing
Using the NSPredicate class worked for me.
let welcomePage = app.staticTexts["Landing Page"]
let existsPredicate = NSPredicate(format: "exists == 1")
expectationForPredicate(existsPredicate, evaluatedWithObject: LandingPageLoad, handler: nil)
waitForExpectationsWithTimeout(5, handler: nil)
First create a constant that of a value or object you're waiting for
Create another constant using the NSPredicate class to compare a value of an
object
Then use the expectationForPredicate method along with vars
And lastly give the handler a timeout upper limit
I refer to this answer and this XCUIElementQuery function can handle the case the element is not created:
open func matching(identifier: String) -> XCUIElementQuery
ref:
Testing if an element is visible with XCode 7 UITest

How to test right-to-left language in iOS XCTest unit tests?

Is there a way to switch an XCTest unit test into the right-to-left mode to test Arabic version of the app where sentences are written from right to left of the screen? My app code logic behaves differently based on language direction. I would like to verify this functionality in a unit test. What I need to do is to switch the app into the right-to-left language mode from an XCTest unit test case.
One can run the app in the right-to-left mode by changing the Scheme's Application language settings to Right-to-left Pseudolanguage. Is there a way to do similar thing in a unit test?
My imperfect solution
I ended up changing semanticContentAttribute of a view under test to .ForceRightToLeft. It does what I need to do. It does not feel like a very clean approach though. Firstly, it only works in iOS 9. Secondly, it looks like I am tinkering with my app views on a low level from the unit test. Instead, I would prefer to switch the whole app's language to right-to-left if it is possible.
class MyTests: XCTestCase {
func testRightToLeft() {
if #available(iOS 9.0, *) {
let view = UIView()
view.semanticContentAttribute = .ForceRightToLeft
// Test code involving the view
}
}
}
There's no easy way to do this right now with testing/UI testing besides passing in environment flags or setting the semanticContentAttribute as you are doing now. Filing a bug to Apple is highly recommended.
You can also change the device language & region in the scheme. This means you'll need separate schemes for the various LTR/RTL tests you want to run:
Xcode even provides pseudo-languages for extra-long string & RTL testing.
You can detect the writing direction via
let writingDirection = UIApplication.sharedApplication().userInterfaceLayoutDirection
switch writingDirection {
case .LeftToRight:
//
case .RightToLeft:
//
default:
break // what now? You are obviously using iOS 11's topToBottom direction…
}
To set different languages and locales on startup this might be a proper solution.
What you are looking for is Automated UI-Testing
This example JavaScript code changes the device orientation for example:
var target = UIATarget.localTarget();
var app = target.frontMostApp();
//set orientation to landscape left
target.setDeviceOrientation(UIA_DEVICE_ORIENTATION_LANDSCAPELEFT);
UIALogger.logMessage("Current orientation now " + app.interfaceOrientation());
//reset orientation to portrait
target.setDeviceOrientation(UIA_DEVICE_ORIENTATION_PORTRAIT);
UIALogger.logMessage("Current orientation now " + app.interfaceOrientation());
For testing, if your layout has changed to RTL or LTR you could try to access specific UI Elements and check their content against an expected content. So here is another example to check the contents of a TableViewCell from the official docs:
The crux of testing is being able to verify that each test has been performed and that it has either passed or failed. This code example runs the test testName to determine whether a valid element recipe element whose name starts with “Tarte” exists in the recipe table view. First, a local variable is used to specify the cell criteria:
var cell = UIATarget.localTarget().frontMostApp().mainWindow() \
.tableViews()[0].cells().firstWithPredicate("name beginswith 'Tarte'");
Next, the script uses the isValid method to test whether a valid element matching those criteria exists in the recipe table view.
if (cell.isValid()) {
UIALogger.logPass(testName);
} else {
UIALogger.logFail(testName);
}
If a valid cell is found, the code logs a pass message for the testName test; if not, it logs a failure message.
Notice that this test specifies firstWithPredicate and "name
beginsWith 'Tarte'". These criteria yield a reference to the cell for
“Tarte aux Fraises,” which works for the default data already in the
Recipes sample app. If, however, a user adds a recipe for “Tarte aux
Framboises,” this example may or may not give the desired results.
If you want to test a specific scheme:
Executing an Automation Instrument Script in Xcode
After you have created your customized Automation template, you can execute your test script from Xcode by following these steps:
Open your project in Xcode.
From the Scheme pop-up menu (in the workspace window toolbar), select Edit Scheme for a scheme with which you would like to use your script.
Select Profile from the left column of the scheme editing dialog.
Choose your application from the Executable pop-up menu.
Choose your customized Automation Instrument template from the Instrument pop-up menu.
Click OK to approve your changes and dismiss the scheme editor dialog.
Choose Product > Profile.
Instruments launches and executes your test script.

Resources