---
title: 'Arayüz Testleri'
source: 'https://academia.sh/tr/kurslar/ios-uretim/arayuz-testleri'
course: 'Mimari, Test ve Yayınlama'
language: tr
updated: '2026-10-21T05:03:08+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Arayüz testinin uygulamayı ayrı bir süreç olarak nasıl sürdüğünü ve öğeleri nasıl bulduğunu anlatır'
    - 'Metne bağlı, varlığa bağlı ve sonuca bağlı üç denetimi ölçerek ayırır'
    - 'Düğmenin gerçekten sipariş gönderdiğini yalnız sonucu denetleyen testin gösterdiğini kanıtlar'
    - 'Bekleme anının aynı denetimi ayıran bir denetimden gürültülü bir denetime çevirdiğini ölçer'
---

# Arayüz Testleri

Uygulamayı ayrı bir süreç olarak sürüp düğmeye basan arayüz testlerinin kurulması ve beklenmesi; üç denetimin ve bekleme anının doğru ve kusurlu sürümü ayırıp ayırmadığının ölçümü.

Birim testi bir işlevi çağırır ve dönen değere bakar. Kullanıcı ise işlev çağırmaz; bir düğmeye basar. Düğmenin arkasında doğru kod çalışsa bile düğme ekrana bağlanmamış olabilir. Önceki derste (Birim Testleri) bir sınır kusurunun yalnız sınır değerini deneyen denetimle yakalandığını gördük. Bu derste kusur başka bir yerde: "Siparişi Onayla" düğmesi görünüyor, basılıyor, ama sipariş göndermiyor.

Ölçü ekseni yine **ayrımdır**: aynı denetimi doğru sürüme ve kusurlu sürüme uygularız. **Ayıran** denetim doğru sürümde geçer, kusurlu sürümde kalır. **Kör** denetim ikisinde de geçer. **Gürültülü** denetim ikisinde de kalır. **Ters** denetim doğru sürümde kalır, kusurlu sürümde geçer. Dört adı çıktıda ASCII olarak göreceğiz. Çekirdek önceki dersteki çekirdeğin aynısıdır ve yeniden yazılmıştır, çünkü her ders kendi başına çalışır.

```swift
// Separation.swift — bir denetimin dogru surumu kusurlu surumden ayirip ayirmadigini siniflandirir
struct Contrast<Output: CustomStringConvertible> {
    let label: String
    let correct: Output
    let faulty: Output
    let passesCorrect: Bool
    let passesFaulty: Bool

    var separation: String {
        switch (passesCorrect, passesFaulty) {
        case (true, false): return "ayiran"
        case (true, true): return "kor"
        case (false, false): return "gurultulu"
        case (false, true): return "ters"
        }
    }

    func describe() -> String {
        let onCorrect = passesCorrect ? "gecti" : "kaldi"
        let onFaulty = passesFaulty ? "gecti" : "kaldi"
        print("\(label) | dogru=\(correct)/\(onCorrect) kusurlu=\(faulty)/\(onFaulty) -> \(separation)")
        return separation
    }
}

func contrast<Output>(_ label: String, correct: Output, faulty: Output, check: (Output) -> Bool) -> Contrast<Output> {
    Contrast(label: label, correct: correct, faulty: faulty, passesCorrect: check(correct), passesFaulty: check(faulty))
}

func countSeparations(_ results: [String]) {
    for name in ["ayiran", "kor", "gurultulu", "ters"] {
        print("\(name): \(results.filter { $0 == name }.count)")
    }
}
```

`Contrast` iki sürümün çıktısını ve denetimin o çıktılar üzerindeki kararını tutar; `separation` etiketi yalnız iki doğruluk değerinden türetir. `contrast` işlevi aynı kapanışı iki çıktıya uygular, `countSeparations` dört kategoriyi sayar. **Kural nedeni:** denetimin değeri geçmesinde değil, iki sürümü ayırmasında yatar.

## Uygulamayı Sürmek

Arayüz testi uygulamanın kodunu çağırmaz. Test hedefi uygulamayı ayrı bir süreç olarak başlatır ve ekrana dışarıdan bir kullanıcı gibi dokunur. Bu yüzden testin içinden uygulamanın tür ve işlevlerine erişilmez; yalnız ekranda görünen öğelere erişilir. Sürücü `XCUIApplication` nesnesidir.

```swift
// siparis onayi arayuz testi — KAYNAK
import XCTest

final class CheckoutUITests: XCTestCase {
    override func setUpWithError() throws {
        continueAfterFailure = false
    }

    func testConfirmButtonExists() {
        let app = XCUIApplication()
        app.launchArguments = ["--reset-orders"]
        app.launch()
        XCTAssertTrue(app.buttons["confirmButton"].exists)
    }

    func testConfirmAddsOrder() {
        let app = XCUIApplication()
        app.launchArguments = ["--reset-orders"]
        app.launch()
        app.buttons["confirmButton"].tap()
        XCTAssertTrue(app.tables["orderList"].cells.element(boundBy: 0).waitForExistence(timeout: 5))
    }
}
```

Bu parça çalıştırılmaz; bir simülatör ya da gerçek aygıt ister. `continueAfterFailure = false` ilk iddia tutmayınca testi durdurur; aksi hâlde boş bir ekrana tıklamaya devam eden test art arda anlamsız hatalar üretir. `launchArguments` uygulamaya testin başlangıç durumunu söyler: burada sipariş listesini boşaltan bir bayrak. Her test aynı durumdan başlamazsa öncekinin bıraktığı kayıt sonuçları değiştirir.

Öğeler üç yolla bulunur. Görünen metinle (`app.buttons["Siparisi Onayla"]`), sıra numarasıyla (`element(boundBy:)`) ya da **erişilebilirlik tanımlayıcısıyla** (accessibility identifier). Tanımlayıcı, öğeye uygulama kodunda verilmiş sabit bir addır ve metin değişse de aynı kalır. Yukarıdaki parçada düğme `confirmButton` tanımlayıcısıyla bulunuyor.

## Beklemek

Ekran anında değişmez. Düğmeye basıldığında sipariş gönderilir, yanıt gelir, liste yenilenir; bu zincir bir süre alır. `waitForExistence(timeout:)` öğenin ekranda belirmesini en çok verilen süre kadar bekler ve `Bool` döndürür. Beklemeden doğrudan `exists` okumak, öğe henüz çizilmemişken yanlış cevap alma riskidir.

Bu derste süreleri ölçmeyeceğiz ve bir testin zaman aşımına ne kadar yaklaştığını göstermeyeceğiz. Zamanlama makineden makineye değişir; ona bağlı bir sayı basmak tekrarlanabilir olmaz. Bunun yerine modelde bekleme iki duruma ayrılır: düğmeye basıldıktan hemen sonraki ekran ve ekranın oturduktan sonraki hâli. Gerçek bir testte bu iki an arasında kaç milisaniye geçeceği ölçülmez; modelde yalnız "önce" ve "sonra" vardır.

## Onay Ekranı Modeli

Aşağıdaki **model**, denetimlerin iki sürüme ne dediğini kurar; arayüz test çerçevesinin kendisi değildir. Ekran bir yapıdır. Düğmenin başlığı, bekleyen siparişler ve ekranda listelenmiş siparişler tutulur. `tapConfirm` düğmeye basmayı, `settle` ekranın oturmasını temsil eder. Doğru sürümde basış bir sipariş ekler; kusurlu sürümde düğmeye basmak hiçbir şey yapmaz.

```swift
// Checkout.swift — onay ekraninin durumu, dogru ve kusurlu surumu
struct CheckoutScreen {
    let confirmTitle = "Siparisi Onayla"
    private(set) var pending: [String] = []
    private(set) var orders: [String] = []

    mutating func tapConfirm(sending: Bool) {
        if sending { pending.append("siparis-1") }
    }

    mutating func settle() {
        orders += pending
        pending = []
    }
}

func screenAfterTap(sending: Bool, settled: Bool) -> CheckoutScreen {
    var screen = CheckoutScreen()
    screen.tapConfirm(sending: sending)
    if settled { screen.settle() }
    return screen
}
```

Ekran oturmuş durumda üç denetim uygulanır. İlki **metne bağlıdır**: düğmenin başlığını, uygulamanın eski sürümündeki yazımla ("Siparisi onayla") karşılaştırır. Metin sonradan bir yazı düzeltmesiyle büyük harfle değişmiştir ve iki sürüm de yeni metni taşır. İkincisi **varlığa bağlıdır**: düğme ekranda var mı. Üçüncüsü **sonuca bağlıdır**: düğmeye basıldıktan sonra sipariş listesi bir sipariş uzamış mı.

```swift
// main.swift — uc denetim, iki surum
let correct = screenAfterTap(sending: true, settled: true)
let faulty = screenAfterTap(sending: false, settled: true)

let oldTitle = contrast("eski-baslik-metni", correct: correct.confirmTitle, faulty: faulty.confirmTitle, check: { $0 == "Siparisi onayla" }).describe()
let exists = contrast("dugme-var", correct: !correct.confirmTitle.isEmpty, faulty: !faulty.confirmTitle.isEmpty, check: { $0 }).describe()
let listGrew = contrast("siparis-listesi-uzadi", correct: correct.orders.count, faulty: faulty.orders.count, check: { $0 == 1 }).describe()

countSeparations([oldTitle, exists, listGrew])
```

```
eski-baslik-metni | dogru=Siparisi Onayla/kaldi kusurlu=Siparisi Onayla/kaldi -> gurultulu
dugme-var | dogru=true/gecti kusurlu=true/gecti -> kor
siparis-listesi-uzadi | dogru=1/gecti kusurlu=0/kaldi -> ayiran
ayiran: 1
kor: 1
gurultulu: 1
ters: 0
```

`eski-baslik-metni` gürültülü: iki sürüm de "Siparisi Onayla" başlığını taşır ve denetim eski yazımı bekler, o yüzden ikisini de reddeder. Düğme çalışıyordur, test kızar. `dugme-var` kör: düğme iki sürümde de ekranda. `siparis-listesi-uzadi` ayıran: doğru sürümde liste bir sipariş taşır, kusurlu sürümde sıfır taşır.

Bu üç denetimin üçü de arayüz testinde yazılabilir ve üçü de bir test raporunda aynı görünür. Biri uygulamadaki kopyalama değişikliğinde boş yere kırılır, biri düğme hiç çalışmasa da yeşil kalır, biri ise düğmenin işini yaptığını söyler. Fark, denetimin neye baktığıdır: metne mi, varlığa mı, sonuca mı.

**TS1.** Ekran modeli tek bir sipariş ekler. Birden çok sipariş, silme ya da iptal ayrıca ölçülmelidir.

**TS2.** `eski-baslik-metni` denetimini gürültülü yapan şey iki sürümün de yeni metni taşımasıdır. Metin yalnız kusurlu sürümde değişseydi aynı denetim ters ya da ayıran olabilirdi.

## Metne Bağlı Denetim

Metne bağlı bir test düğmeyi görünen metniyle bulur. Bu rahattır, çünkü ekranda ne yazıyorsa testte de odur. Ama metin pazarlamanın, çevirinin ya da yazı düzeltmesinin elindedir. Başlık değiştiğinde uygulama doğru çalışmaya devam eder, test ise kızar. Bu tür bir kırılma bir kusuru göstermez; bakım yükü yaratır.

Üstelik çok dilli bir uygulamada aynı düğmenin metni dile göre değişir. Metne bağlı bir test yalnız yazıldığı dilde çalışır. Test planının ikinci bir dil yapılandırması eklendiğinde metne bağlı testler ikinci dilde kalır; bu konunun sonraki dersinde ölçeceğiz. Tanımlayıcıyla bulunan düğme ise her dilde aynı adı taşır.

Metni testten tamamen atmak da çözüm değildir. Bazı testler metnin kendisini sınar: sipariş onaylandığında ekranda "Siparişiniz alındı" yazması bir davranıştır. Fark şuradadır: bir öğeyi bulmak için metin kullanmak bakım yükü getirir, bir davranışı doğrulamak için kullanmak getirmez.

## Sonuca Bağlı Denetim

`siparis-listesi-uzadi` ayırdı, çünkü düğmenin ürettiği etkiye baktı. Düğme gerçekten çalışıyorsa liste uzar; çalışmıyorsa uzamaz. Bir arayüz testi yazarken ilk soru "düğme var mı" değil "düğmeye basınca ne olmalı" olmalıdır.

Sonuca bağlı denetimin bedeli vardır. Sipariş listesinin ekranda göründüğü yer değişirse, yeni bir sekmeye taşınırsa ya da tablo bir koleksiyona dönüşürse test yine kırılır. Bu yüzden tanımlayıcı sabit tutulur ve ekranın yapısı değişirken tanımlayıcı taşınır. Sonuca bağlı denetim ayırır ama bakımı da ister.

Arayüz testleri yavaştır. Her biri uygulamayı başlatır, ekranı sürer ve bitirir. Bu yüzden sayıları az, kapsamları geniş tutulur: birim testleri kod mantığını sınar, arayüz testleri uçtan uca birkaç temel yolu sınar. Bir sınır kusuru için arayüz testi yazmak yanlış araçtır; bir bağlantı kusuru için birim testi yazmak da öyle.

## Sınırlayıcı Ölçüm

Aynı sonuca bağlı denetim, bekleme anına göre farklı davranır. Aşağıda `siparis-listesi-uzadi` denetimi iki ekranda uygulanır: düğmeye basılır basılmaz bakılan ekranda ve ekran oturduktan sonra bakılan ekranda. İlkine `anlik-bakis`, ikincisine `bekledikten-sonra` adını verdik.

```swift
// main.swift — ayni denetim, iki bakis ani
let immediateCorrect = screenAfterTap(sending: true, settled: false)
let immediateFaulty = screenAfterTap(sending: false, settled: false)
let settledCorrect = screenAfterTap(sending: true, settled: true)
let settledFaulty = screenAfterTap(sending: false, settled: true)

let immediate = contrast("anlik-bakis", correct: immediateCorrect.orders.count, faulty: immediateFaulty.orders.count, check: { $0 == 1 }).describe()
let afterWait = contrast("bekledikten-sonra", correct: settledCorrect.orders.count, faulty: settledFaulty.orders.count, check: { $0 == 1 }).describe()

countSeparations([immediate, afterWait])
```

```
anlik-bakis | dogru=0/kaldi kusurlu=0/kaldi -> gurultulu
bekledikten-sonra | dogru=1/gecti kusurlu=0/kaldi -> ayiran
ayiran: 1
kor: 0
gurultulu: 1
ters: 0
```

Aynı denetim, ekran oturmadan uygulandığında gürültülü oldu: doğru sürüm bile sıfır sipariş gösterir ve test kalır. Bekledikten sonra aynı denetim ayırandır. Denetimin değeri yalnız neye baktığına değil, ne zaman baktığına da bağlıdır.

Modelde iki an kesin ve belirlenimlidir: önce ve sonra. Gerçek bir uygulamada oturma anı değişir; test bazı koşumlarda beklemeyi yetiştirir, bazılarında yetiştiremez. Böyle sonuçları belirsiz olan teste **kırılgan test** (flaky test) denir ve bu dersin modelinde basılmaz; çünkü model her koşumda aynı sonucu verir ve belirsizliği temsil etmez. Kırılgan bir testi çözmek için `waitForExistence(timeout:)` gibi açık beklemeler kullanılır; keyfi bir süre uyumak ise sorunu gizler.

**TS3.** Model oturmayı tek bir adım sayar. Gerçekte liste birden çok adımda yenilenebilir ve ara durumlar görülebilir.

**TS4.** `anlik-bakis` denetiminin gürültülü çıkması modelin iki ekranı da aynı anda boş saymasından gelir. Gerçek bir koşumda ekran bazen hazır olabilir ve sonuç değişir.

## Sık Yapılan Hatalar

- Düğmeyi yalnız metniyle bulmak. Başlık değiştiğinde ya da uygulama başka dilde açıldığında test kırılır.
- Yalnız düğmenin ekranda olduğunu denetlemek. Kör kalır ve işlevsiz düğmeyi kaçırır.
- Sabit süre uyuyarak beklemek. Süre kısaysa test kırılgan olur, uzunsa gereksiz yavaşlar.
- Her testi uygulamanın önceki koşumdan kalan durumundan başlatmak. Başlangıç durumu bayraklarla sıfırlanmalıdır.
- Bir sınır kusurunu arayüz testiyle aramak. Bunun için birim testi daha hızlı ve daha kesindir.

## Özet

- Arayüz testi uygulamayı ayrı bir süreç olarak sürer ve ekrana yalnız görünen öğeler üzerinden erişir.
- Öğeler metinle, sıra numarasıyla ya da erişilebilirlik tanımlayıcısıyla bulunur; tanımlayıcı metin değişse de aynı kalır.
- Üç denetimden biri gürültülü, biri kör, biri ayıran çıktı; yalnız sonuca bağlı denetim düğmenin sipariş gönderdiğini gösterdi.
- Aynı denetim, ekran oturmadan uygulandığında gürültülü oldu; bekleme anı denetimin kategorisini değiştirdi.
- Sonuç belirsiz olan kırılgan testler modelde basılmaz; çözümleri açık beklemedir, keyfi süre değil.
- Bu dersin ayrımı: **gürültülü**.

## Sonraki Adım

Bu derste denetimleri tek tek yazdık. Sıradaki derste Test Planları ve Kapsam, tüm testleri bir plan altında toplayıp hangi satırların koştuğunu sayacak ve koşan bir satırın doğru olduğunu söylemediğini ölçecek.
