How to run UI Test with Facebook login
Issue #44
Today I’m trying to run some UITest on my app, which uses Facebook login. And here are some of my notes on it.
Challenges
- The challenges with Facebook is it uses
Safari controller, we we deal mostly withweb viewfor now. Starting from iOS 9+, Facebook decided to usesafariinstead ofnative facebook appto avoid app switching. You can read the detail here Building the Best Facebook Login Experience for People on iOS 9 - It does not have wanted
accessibilityIdentifieroraccessibilityLabel - The webview content may change in the future 😸
Create a Facebook test user
Luckily, you don’t have to create your own Facebook user to test. Facebook supports test users that you can manage permissions and friends, very handy

When creating the test user, you have the option to select language. That will be the displayed language in Safari web view. I choose Norwegian 🇳🇴 for now

Click the login button and show Facebook login
Here we use the default FBSDKLoginButton
1 | var showFacebookLoginFormButton: XCUIElement { |
And then tap it
1 | app.showFacebookLoginFormButton.tap() |
Check login status
When going to safari Facebook form, user may have already logged in or not. So we need to handle these 2 cases. When user has logged in, Facebook will say something like “you have already logged in” or the OK button.
The advice here is to put breakpoint and po app.staticTexts, po app.buttons to see which UI elements are at a certain point.
You can check for the static text, or simply just the OK button
1 | var isAlreadyLoggedInSafari: Bool { |
Wait and refresh
But Facebook form is a webview, so its content is a bit dynamic. And UITest seems to cache content for fast query, so before checking staticTexts, we need to wait and refresh the cache
1 | app.clearCachedStaticTexts() |
This is the wait function
1 | extension XCTestCase { |
Wait for element to appear
But a more solid approach would be to wait for element to appear. For Facebook login form, they should display a Facebook label after loading. So we should wait for this element
1 | extension XCTestCase { |
And call this before you do any further inspection on elements in Facebook login form
1 | wait(for: app.staticTexts["Facebook"], timeout: 5) |
If user is logged in
After login, my app shows the main controller with a map view inside. So a basic test would be to check the existence of that map
1 | if app.isAlreadyLoggedInSafari { |
Handle interruption
You know that when showing the map with location, Core Location will ask for permission. So we need to handle that interruption as well. You need to ensure to call it early before the alert happens
1 | fileprivate func handleLocationPermission() { |
There is another problem, this monitor won’t be called. So the workaround is to call app.tap() again when the alert will happen. In my case, I call app.tap() when my map has been shown for 1,2 seconds, just to make sure app.tap() is called after alert is shown
For a more detailed guide, please read https://github.com/onmyway133/blog/issues/48
If user is not logged in
In this case, we need to fill in email and password. You can take a look at the The full source code section below. When things don’t work or po does not show you the elements you needed, it’s probably because of caching or you need to wait until dynamic content finishes rendering.
You need to wait for element to appear
Tap on the text field
You may get Neither element nor any descendant has keyboard focus, here are the workaround
- If you test on Simulator, make sure
Simulator -> Hardware -> Keyboard -> Connect Hardware Keyboardis not checked waita bit after tap
1 | app.emailTextField.tap() |
Clear all the text
The idea is to move the caret to the end of the textField, then apply each delete key for each character, then type the next text
1 | extension XCUIElement { |
Change language
For my case, I want to test in Norwegian, so we need to find the Norwegian option and tap on that. It is identified as static text by UI Test
1 | var norwegianText: XCUIElement { |
1 | wait(for: app.norwegianText, timeout: 1) |
The email text field
Luckily, email text field is detected by UI Test as text field element, so we can query for that. This uses predicate
1 | var emailTextField: XCUIElement { |
The password text field
UI Test can’t seem to identify the password text field, so we need to search for it by coordinate
1 | var passwordCoordinate: XCUICoordinate { |
This is the document for func coordinate(withNormalizedOffset normalizedOffset: CGVector) -> XCUICoordinate
Creates and returns a new coordinate with a normalized offset.
The coordinate’s screen point is computed by adding normalizedOffset multiplied by the size of the element’s frame to the origin of the element’s frame.
Then type the password
1 | app.passwordCoordinate.tap() |
We should not use app.passwordCoordinate.referencedElement because it will point to email text field ❗️ 😢
Run that test again
Go to Xcode -> Product -> Perform Actions -> Test Again to run the previous test again

The full source code
1 | import XCTest |
Read more
I found these guides to cover many aspects of UITests, worth taking a look