Kuinka yksikkötestata yksityisiä metodeja Swiftissä

Lataa ilmainen kopio kirjasta
The Missing Manual
for Swift Development

The Guide I Wish I Wish I had When I Started Out

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy

Swift-projektin yksikkötestaus on aivan erilaista kuin Objective-C:llä kirjoitetun projektin yksikkötestaus. Niille, jotka ovat tottuneet Objective-C:n ajoajan joustavuuteen, voi tuntua siltä, että kädet on sidottu selän taakse.

Käytönvalvonta

Kaikkakin käytönvalvonta on erittäin tervetullut lisäys, jolla on monia hyötyjä, se voi hankaloittaa yksikkötestausta, varsinkin jos yksikkötestaaminen on uutta. Tiedät luultavasti, että voit soveltaa testable-attribuuttia testikohteen import-lausekkeeseen saadaksesi pääsyn olioihin, jotka on ilmoitettu sisäisiksi.

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

Niin kätevä lisäys tämä on, mutta se ei anna sinulle pääsyä testikohteen yksityisiin olioihin. Tästä pääsemmekin päivän kysymykseen. Miten yksikkötestaat yksityisiä olioita?

Väärä kysymys

Lyhyt vastaus tähän kysymykseen on yksinkertainen. Et voi käyttää yksityisiä olioita toisesta moduulista ja tämä koskee myös testikohteita. Yksinkertaisesti ja yksinkertaisesti. Sitä varten on pääsynvalvonta.

Mutta se ei ole vastaus kysymykseen. Jos kysyt, miten yksikkötestaat yksityisiä olioita, niin kysyt väärän kysymyksen. Mutta miksi?

None of Your Business

Miksi julistat olion yksityiseksi? Mikä on motiivisi tehdä niin? Katso seuraavaa esimerkkiä.

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

Haluan yksikkötestata AccountViewViewModel-rakennetta. Kuten näet, AccountViewViewModel-rakenne paljastaa kaksi sisäistä laskettua ominaisuutta ja se määrittelee myös yksityisen metodin. expiresAtAsString laskennallinen ominaisuus siirtää osan työstään yksityiselle parse(date:)-metodille. Sisäisten laskennallisten ominaisuuksien testaaminen on suoraviivaista.

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

Mutta miten testaamme yksityisen metodin? Emme voi käyttää yksityistä metodia testikohteesta. Mutta miksi meidän pitäisi yksikkötestata yksityinen metodi? Merkitsimme sen yksityiseksi syystä. Eikö niin? Ja se tuo meidät vastaukseen kysymykseen, jolla aloitimme. Emme testaa yksityisiä metodeja.

Yksikkötestaamalla julkisen rajapinnan

Yksikkötestaamalla AccountViewViewModel-rakenteen julkisen rajapinnan yksikkötestaamme automaattisesti tai epäsuorasti rakenteen yksityisen rajapinnan. Sinun tehtäväsi on varmistaa, että julkinen rajapinta testataan perusteellisesti. Tämä tarkoittaa, että sinun on varmistettava, että yksikkötestit kattavat kaikki AccountViewViewModel-rakenteen koodipolut. Toisin sanoen yksikkötestisarjan pitäisi johtaa täydelliseen koodin kattavuuteen. Tämä sisältää julkiset, sisäiset ja yksityiset oliot.

Jos otamme koodin kattavuuden käyttöön Xcodessa ja suoritamme AccountViewViewModel structin yksikkötestit, huomaamme, että joitakin koodipolkuja ei suoriteta.

Koodin kattavuus Xcodessa

Tämä kertoo, että yksikkötestit ovat puutteellisia. Voimme sivuuttaa koodipolun kohtalokkaan virheen. En koskaan yksikkötestaa koodipolkuja, jotka johtavat fataaliin virheeseen, mutta tämä riippuu pitkälti siitä, miten käytät fataaleja virheitä projekteissasi.

Voimme lisätä koodin kattavuutta AccountViewViewModel-rakenteelle lisäämällä yhden yksikkötestin lisää.

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

Koodin kattavuus Xcodessa

Toteutus ja spesifikaatio

On tärkeää ymmärtää, että testaamme AccountViewViewModel-rakenteen spesifikaatiota. Emme testaa sen toteutusta. Vaikka tämä saattaa kuulostaa samankaltaiselta, se on itse asiassa hyvin erilainen. Me testaamme AccountViewViewModel-rakenteen toiminnallisuutta. Emme ole kiinnostuneita siitä, miten se tekee taikojaan konepellin alla.

Tämän artikkelin tärkein johtopäätös on, että yksityisiä olioita ei tarvitse yksikkötestata. Yksikkötestaus on eräänlaista mustan laatikon testausta. Tämä tarkoittaa, että emme testaa AccountViewViewModel-rakenteen toteutusta, vaan sen määrittelyä.

Tämä ei kuitenkaan tarkoita, ettemme olisi kiinnostuneita toteutuksesta. Meidän on varmistettava, että yksikkötestisarja kattaa testaamamme olion jokaisen koodipolun. Koodin kattavuusraportit ovat korvaamattoman arvokkaita tämän saavuttamisessa.

Lataa ilmainen kopio kirjasta
The Missing Manual
for Swift Development

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

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy

Vastaa

Sähköpostiosoitettasi ei julkaista.