Manualului lipsă
pentru dezvoltarea Swift
Ghidul pe care aș fi vrut să-l am când am început
Alăturați-vă 20,000+ Developers Learning About Swift Development
Download Your Free Copy
Testarea unitară a unui proiect Swift este destul de diferită de testarea unitară a unui proiect scris în Objective-C. Pentru cei care sunt obișnuiți cu flexibilitatea timpului de execuție Objective-C, s-ar putea să se simtă ca și cum ar avea mâinile legate la spate.
Controlul accesului
În timp ce controlul accesului este o adăugire foarte binevenită, cu multe beneficii, poate complica testarea unitară, mai ales dacă sunteți nou în testarea unitară. Probabil că știți că puteți aplica atributul testable
la o instrucțiune de import într-o țintă de testare pentru a obține acces la entitățile care sunt declarate ca fiind interne.
import XCTest@testable import Notesclass NotesTests: XCTestCase { ...}
În timp ce aceasta este o adăugare convenabilă, nu vă oferă acces la entitățile private dintr-o țintă de testare. Acest lucru ne aduce la întrebarea zilei. Cum testați unitar entitățile private?
Întrebare greșită
Răspunsul scurt la această întrebare este simplu. Nu puteți accesa entități private dintr-un alt modul și acest lucru este valabil și pentru țintele de test. Pur și simplu. Pentru asta există controlul accesului.
Dar acesta nu este răspunsul la întrebare. Dacă întrebați cum să testați unitar entitățile private, atunci puneți o întrebare greșită. Dar de ce oare?
Nu este treaba ta
De ce declarați o entitate privată? Care este motivația ta pentru a face acest lucru? Aruncați o privire la următorul exemplu.
import Foundationstruct AccountViewViewModel { // MARK: - Properties let account: Account // MARK: - Public Interface var subscriptionAsString: String { switch account.subscription { case .monthly: return "Monthly Subscription" case .yearly: return "Yearly Subscription" case .trial: return "Trial Subscription" } } var expiresAtAsString: String { // Parse Date let date = parse(date: account.expiresAt) // Initialize Date Formatter let dateFormatter = DateFormatter() // Configure Date Formatter dateFormatter.dateFormat = "YYYY, MMMM" // Convert Date to String return dateFormatter.string(from: date) } // MARK: - Private Interface private func parse(date dateAsString: String) -> Date { let dateFormat: String if dateAsString.contains("/") { dateFormat = "YYYY'/'MM'/'dd" } else { dateFormat = "YYYYMMdd" } // Initialize Date Formatter let dateFormatter = DateFormatter() // Configure Date Formatter dateFormatter.dateFormat = dateFormat if let date = dateFormatter.date(from: dateAsString) { print(date) return date } else { fatalError("Incompatible Date Format") } }}
Am dori să testez unitar structura AccountViewViewModel
. După cum puteți vedea, structura AccountViewViewModel
expune două proprietăți interne calculate și definește, de asemenea, o metodă privată. Proprietatea calculată expiresAtAsString
transferă o parte din activitatea sa către metoda privată parse(date:)
. Testarea proprietăților interne calculate este simplă.
// MARK: - Tests for Subscription as Stringfunc testSubscriptionAsString_Monthly() { let account = Account(expiresAt: "20161225", subscription: .monthly) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.subscriptionAsString, "Monthly Subscription")}func testSubscriptionAsString_Yearly() { let account = Account(expiresAt: "20161225", subscription: .yearly) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.subscriptionAsString, "Yearly Subscription")}func testSubscriptionAsString_Trial() { let account = Account(expiresAt: "20161225", subscription: .trial) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.subscriptionAsString, "Trial Subscription")}// MARK: - Tests for Expires at as Stringfunc testExpiresAtAsString_20161225() { let account = Account(expiresAt: "20161225", subscription: .trial) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.expiresAtAsString, "2016, December")}
Dar cum testăm metoda privată? Nu putem accesa metoda privată din ținta de testare. Dar de ce ar trebui să testăm unitar metoda privată? Am marcat-o ca fiind privată pentru un motiv. Nu-i așa? Și asta ne aduce la răspunsul la întrebarea cu care am început. Nu testăm metodele private.
Testarea unitară a interfeței publice
Testând unitar interfața publică a structurii AccountViewViewModel
testăm unitar automat sau implicit și interfața privată a structurii. Aveți sarcina de a vă asigura că interfața publică este testată temeinic. Acest lucru înseamnă că trebuie să vă asigurați că fiecare traseu de cod al structurii AccountViewViewModel
este acoperit de teste unitare. Cu alte cuvinte, suita de teste unitare trebuie să aibă ca rezultat o acoperire completă a codului. Aceasta include entitățile publice, interne și private.
Dacă activăm acoperirea codului în Xcode și rulăm testele unitare ale structurii AccountViewViewModel
struct, putem vedea că unele căi de cod nu sunt executate.
Acest lucru ne spune că testele unitare sunt incomplete. Putem ignora calea de cod pentru eroarea fatală. Niciodată nu testez unitar căile de cod care au ca rezultat o eroare fatală, dar acest lucru depinde în mare măsură de modul în care utilizați erorile fatale în proiectele dumneavoastră.
Potem crește acoperirea codului pentru structura AccountViewViewModel
prin adăugarea unui test unitar suplimentar.
func testExpiresAtAsString_20161225WithForwardSlashes() { let account = Account(expiresAt: "2016/12/25", subscription: .trial) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.expiresAtAsString, "2016, December")}
Implementare și specificație
Este important să înțelegem că testăm specificația structurii AccountViewViewModel
. Nu testăm implementarea acesteia. Deși acest lucru poate părea similar, este de fapt foarte diferit. Noi testăm funcționalitatea structurii AccountViewViewModel
. Nu suntem interesați de modul în care își face magia sub capotă.
Din acest articol reiese că entitățile private nu au nevoie să fie testate unitar. Testarea unitară este o formă de testare black-box. Aceasta înseamnă că nu testăm implementarea structurii AccountViewViewModel
, ci îi testăm specificația.
Aceasta nu înseamnă că nu suntem interesați de implementare, totuși. Trebuie să ne asigurăm că suita de teste unitare acoperă fiecare cale de cod a entității pe care o testăm. Rapoartele de acoperire a codului sunt de neprețuit pentru a realiza acest lucru.
The Missing Manual
for Swift Development
The Guide I Wish I Had When I Started Out
Alăturați-vă celor peste 20.000 de dezvoltatori care învață despre dezvoltarea Swift
Descărcați copia dvs. gratuită
.