4.0 KiB
UI Testing
Running Tests
xcodebuild test \
-project Telegram/Telegram.xcodeproj \
-scheme iOSAppUITestSuite \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=26.1'
Pick any available simulator. List them with xcrun simctl list devices available iPhone.
Test Environment
Tests launch the app with the --ui-test argument. When the app detects this flag:
- It uses an isolated data directory (
telegram-ui-tests-data/inside the app group container), completely separate from production data. - That directory is wiped on every launch, so each test run starts with a clean slate.
- The app connects to Telegram test servers (not production). Test server accounts are independent of production accounts.
This means every test begins with the app in its first-launch state: no accounts, no data, showing the welcome/auth screen.
Writing Tests
Test files live in Telegram/Tests/Sources/. The test target is iOSAppUITestSuite.
Tests use Apple's XCUITest framework. Each test class extends XCTestCase and interacts with the app through XCUIApplication.
Template
import XCTest
class MyFeatureTests: XCTestCase {
private var app: XCUIApplication!
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
app.launchArguments.append("--ui-test")
}
override func tearDownWithError() throws {
app = nil
}
func testSomething() throws {
app.launch()
// Find elements
let button = app.buttons["Start Messaging"]
// Wait for elements to appear
XCTAssert(button.waitForExistence(timeout: 5.0))
// Interact
button.tap()
// Assert
XCTAssert(app.textFields["Phone Number"].waitForExistence(timeout: 3.0))
}
}
Key Patterns
Always pass --ui-test in launchArguments. Without it, the app uses production servers and the real database.
Wait for elements rather than assuming they exist immediately. Use element.waitForExistence(timeout:) before interacting.
Find elements by accessibility identifier, label, or type:
app.buttons["Continue"] // by label
app.textFields["Phone Number"] // by placeholder/label
app.staticTexts["Welcome"] // static text
app.navigationBars["Settings"] // navigation bar
Relaunch between tests if needed. Each app.launch() call with --ui-test wipes the database, so every test method that calls launch() gets a fresh app state.
Type text into fields:
let field = app.textFields["Phone Number"]
field.tap()
field.typeText("9996621234")
Adding a New Test File
- Create a new
.swiftfile inTelegram/Tests/Sources/. - The file is automatically picked up by the
iOSAppUITestSuitetarget via the Bazel build — no manual target membership changes needed. - Run with the same
xcodebuild testcommand.
Telegram Test Servers
The test environment uses 3 separate Telegram datacenters, completely independent from production.
OS Environment
Test logins are guarded behind a specialized OS environment. The simulator or device must be configured for the test environment before test accounts can authenticate.
Test Phone Numbers
Test phone numbers follow the format 99966XYYYY:
Xis the DC number (1, 2, or 3)YYYYare random digits
The country code for test numbers is 999, and the remaining digits are 66XYYYY.
Examples: +999 66 2 1234, +999 66 1 0000, +999 66 3 0001.
Verification Codes
Test accounts do not receive real SMS. The confirmation code is the DC number repeated 5 times:
- DC 1 (
+999 661 YYYY) -> code11111 - DC 2 (
+999 662 YYYY) -> code22222 - DC 3 (
+999 663 YYYY) -> code33333
Flood Limits
Test numbers are still subject to flood limits. If a number gets rate-limited, pick a different YYYY suffix.
Security
Do not store any important or private information in test accounts. Anyone can use the simplified authorization mechanism to access them.