How to Unit Test Private Methods in Swift

Download Your Free Copy of
The Missing Manual
for Swift Development

The Guide I Wish I Wish I Have When I Started Out

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy

Egy Swift projekt egységtesztelése egészen más, mint egy Objective-C-ben írt projekt egységtesztelése. Azok számára, akik az Objective-C futásidő rugalmasságához szoktak hozzá, úgy érezhetik, mintha a kezük meg lenne kötve a hátuk mögött.

A hozzáférési vezérlés

Míg a hozzáférési vezérlés egy nagyon üdvözlendő, sok előnnyel járó kiegészítés, bonyolíthatja az egységtesztelést, különösen, ha újak vagyunk az egységtesztelésben. Valószínűleg tudod, hogy a testable attribútumot alkalmazhatod egy import utasításhoz egy tesztcélban, hogy hozzáférj a belsőnek deklarált entitásokhoz.

import XCTest@testable import Notesclass NotesTests: XCTestCase { ...}

Míg ez egy kényelmes kiegészítés, nem ad hozzáférést a tesztcélban lévő privát entitásokhoz. Ezzel elérkeztünk a nap kérdéséhez. Hogyan lehet a privát entitásokat egységtesztelni?

Téves kérdés

A kérdésre a rövid válasz egyszerű. A privát entitásokhoz nem férhetsz hozzá egy másik modulból, és ez a tesztcélokra is vonatkozik. Egyszerű és világos. Erre való a hozzáférés-szabályozás.

De nem ez a válasz a kérdésre. Ha azt kérdezed, hogyan lehet a privát entitásokat egységtesztelni, akkor rossz kérdést teszel fel. De miért is?

None of Your Business

Miért nyilvánítasz egy entitást privátnak? Mi a motivációd arra, hogy ezt tedd? Nézzük meg a következő példát:

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") } }}

A AccountViewViewModel struktúrát szeretném egységtesztelni. Mint látható, a AccountViewViewModel struktúra két belső számított tulajdonságot tár fel, és egy privát metódust is definiál. A expiresAtAsString számított tulajdonság a munka egy részét a privát parse(date:) metódusra terheli. A belső számított tulajdonságok tesztelése egyszerű.

// 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")}

De hogyan teszteljük a privát metódust? A privát metódushoz nem férhetünk hozzá a tesztcélból. De miért is kellene egységtesztelnünk a privát metódust? Nem véletlenül jelöltük privátnak. Ugye? És ezzel el is érkeztünk a válaszhoz a kérdésre, amivel kezdtük. Nem teszteljük a privát metódusokat.

A nyilvános interfész egységtesztelése

Azzal, hogy egységteszteljük a AccountViewViewModel struktúra nyilvános interfészét, automatikusan vagy implicit módon egységteszteljük a struktúra privát interfészét. Az a feladatunk, hogy a nyilvános interfészt alaposan teszteljük. Ez azt jelenti, hogy a AccountViewViewModel struktúra minden kódútját le kell fednie egységtesztekkel. Más szóval, az egységtesztek csomagjának teljes kódlefedettséget kell eredményeznie. Ez magában foglalja a nyilvános, a belső és a privát entitásokat.

Ha engedélyezzük a kódlefedettséget az Xcode-ban, és lefuttatjuk a AccountViewViewModel struct egységtesztjeit, láthatjuk, hogy néhány kódútvonal nem kerül végrehajtásra.

Kódlefedettség az Xcode-ban

Ez azt mutatja, hogy az egységtesztek hiányosak. A végzetes hiba miatt figyelmen kívül hagyhatjuk a kódútvonalat. Soha nem szoktam egységtesztelni a végzetes hibát eredményező kódútvonalakat, de ez nagyban függ attól, hogy Ön hogyan használja a végzetes hibákat a projektjeiben.

A AccountViewViewModel struktúra kódlefedettségét növelhetjük egy további egységteszt hozzáadásával.

func testExpiresAtAsString_20161225WithForwardSlashes() { let account = Account(expiresAt: "2016/12/25", subscription: .trial) let accountViewViewModel = AccountViewViewModel(account: account) XCTAssertEqual(accountViewViewModel.expiresAtAsString, "2016, December")}

Kódlefedettség az Xcode-ban

Implementáció és specifikáció

Fontos megérteni, hogy a AccountViewViewModel struktúra specifikációját teszteljük. Nem a megvalósítását teszteljük. Bár ez hasonlóan hangzik, valójában nagyon is különbözik. A AccountViewViewModel struktúra funkcionalitását teszteljük. Nem érdekel bennünket, hogy hogyan varázsol a motorháztető alatt.

A cikk legfontosabb tanulsága, hogy a privát entitásokat nem kell egységtesztelni. A unit tesztelés a fekete dobozos tesztelés egy formája. Ez azt jelenti, hogy nem a AccountViewViewModel struktúra implementációját teszteljük, hanem a specifikációját.

Ez azonban nem jelenti azt, hogy nem érdekel minket az implementáció. Biztosítanunk kell, hogy az egységtesztek csomagja lefedje a tesztelt entitás minden kódútját. A kódlefedettségi jelentések felbecsülhetetlen értéket képviselnek ennek elérésében.

Töltse le a
The Missing Manual
for Swift Development

The Guide I Wish I Wish I Have When I Started Out

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.