# Unit Testing & QA Cheat Sheet

iOS loyihalarda kod sifatini ta'minlash, `XCTest` va zamonaviy `Swift Testing` (Swift 6 / iOS 18+) frameworklari hamda Mocking bo'yicha tezkor qo'llanma.

---

## 1. XCTest Asoslari va Hayotiy Sikli

```swift
import XCTest
@testable import MyApp

final class CalculatorTests: XCTestCase {
    var sut: Calculator! // System Under Test

    // Har bir testdan oldin ishlaydi
    override func setUpWithError() throws {
        try super.setUpWithError()
        sut = Calculator()
    }

    // Har bir test tugagach xotirani tozalash uchun ishlaydi
    override func tearDownWithError() throws {
        sut = nil
        try super.tearDownWithError()
    }

    func testAddition_twoNumbers_returnsSum() {
        // Given (Berilgan shartlar)
        let a = 5
        let b = 10

        // When (Amal bajarilganda)
        let result = sut.add(a, b)

        // Then (Natija tekshiruvi)
        XCTAssertEqual(result, 15, "Yig'indi to'g'ri hisoblanmadi")
    }
}
```

---

## 2. XCTest Assertions (Tekshiruvlar Jadvali)

| Assertion | Maqsadi | Misol |
|---|---|---|
| `XCTAssertTrue(x)` | Shart `true` bo'lishini talab qiladi | `XCTAssertTrue(user.isValid)` |
| `XCTAssertFalse(x)` | Shart `false` bo'lishini talab qiladi | `XCTAssertFalse(cart.isEmpty)` |
| `XCTAssertEqual(a, b)` | Ikkala qiymat teng bo'lishi kerak | `XCTAssertEqual(items.count, 3)` |
| `XCTAssertNotEqual(a, b)`| Qiymatlar bir-biriga teng bo'lmasligi kerak | `XCTAssertNotEqual(oldToken, newToken)` |
| `XCTAssertNil(x)` | Obyekt `nil` bo'lishi kerak | `XCTAssertNil(error)` |
| `XCTAssertNotNil(x)` | Obyekt `nil` bo'lmasligi shart | `XCTAssertNotNil(result)` |
| `XCTAssertThrowsError(expr)` | Ifoda xatolik (throw) berishi kerak | `XCTAssertThrowsError(try parse(""))` |
| `XCTAssertNoThrow(expr)` | Hech qanday xatolik bermasligi kerak | `XCTAssertNoThrow(try save())` |

---

## 3. Asinxron Kodni Testlash

### async/await bilan (Zamonaviy):
```swift
func testFetchUserProfile_success() async throws {
    let mockService = MockUserService()
    mockService.stubbedUser = User(id: 1, name: "Azimjon")
    let viewModel = ProfileViewModel(service: mockService)

    await viewModel.loadUser()

    XCTAssertEqual(viewModel.userName, "Azimjon")
}
```

### Expectation bilan (Callback / Notification / Delegate):
```swift
func testTimerCompletes() {
    let exp = expectation(description: "Taymer tugashi kerak")

    sut.startTimer(duration: 0.1) {
        exp.fulfill()
    }

    wait(for: [exp], timeout: 1.0)
}
```

---

## 4. Swift Testing (Swift 6 / iOS 18+ Yangi Standart)

Apple'ning makrolarga asoslangan eng yangi deklarativ test frameworki.

```swift
import Testing
@testable import MyApp

@Suite("Foydalanuvchi hisob-kitob testlari")
struct UserTests {

    @Test("Yangi foydalanuvchi hisobi balansi 0 bo'lishi kerak")
    func initialBalanceIsZero() {
        let account = BankAccount()
        #expect(account.balance == 0)
    }

    @Test("Pul yechish yetarli mablag' bo'lmaganda xato berishi kerak")
    func withdrawInsufficientFunds() throws {
        var account = BankAccount(initial: 50)
        #expect(throws: BankError.insufficientFunds) {
            try account.withdraw(100)
        }
    }

    // Argumentlar bilan (Parameterized Tests)
    @Test("Yosh bo'yicha toifani aniqlash", arguments: [
        (age: 10, expected: "Bola"),
        (age: 16, expected: "O'smir"),
        (age: 25, expected: "Katta")
    ])
    func ageCategories(age: Int, expected: String) {
        #expect(categorize(age: age) == expected)
    }
}
```

---

## 5. Test Doubles (Mock, Stub, Spy, Fake)

| Tur | Tavsif |
|---|---|
| **Dummy** | Shunchaki parametrni to'ldirish uchun uzatiladigan bo'sh obyekt |
| **Stub** | Har doim oldindan tayyorlangan aniq qiymatni qaytaruvchi soxta obyekt |
| **Spy** | Metod necha marta va qanday parametrlar bilan chaqirilganini yozib boruvchi |
| **Mock** | Chaqirilishi kutilayotgan metodlar va xatti-harakatlarni o'zida tekshiruvchi |
| **Fake** | Ishlaydigan yengilroq sodda implementatsiya (masalan, In-Memory DB) |

```swift
// Spy & Stub Namunasi:
final class MockAuthService: AuthServiceProtocol {
    // Stub
    var loginResult: Result<User, Error> = .success(User(id: "1"))
    
    // Spy (Chaqiruvlarni kuzatish)
    private(set) var loginCallCount = 0
    private(set) var lastEmailAttempt: String?

    func login(email: String, pass: String) async throws -> User {
        loginCallCount += 1
        lastEmailAttempt = email
        return try loginResult.get()
    }
}
```

---

## 6. UI Testing (`XCUIApplication`)

```swift
final class LoginUITests: XCTestCase {
    func testLoginButtonNavigatesToHome() {
        let app = XCUIApplication()
        app.launch()

        let emailField = app.textFields["email_text_field"]
        emailField.tap()
        emailField.typeText("test@example.com")

        let loginButton = app.buttons["login_button"]
        XCTAssertTrue(loginButton.exists)
        loginButton.tap()

        let welcomeLabel = app.staticTexts["welcome_label"]
        XCTAssertTrue(welcomeLabel.waitForExistence(timeout: 2.0))
    }
}
```
