Unit Testing & QA
Cheat Sheet'lar
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
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):
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):
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.
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) |
// 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)
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))
}
}