Výukový program pro unit testování a testování uživatelského rozhraní iOS

Aktualizace: Michael Katz aktualizoval tento výukový program pro Xcode 10.1, Swift 4.2 a iOS 12. Původní text napsala Audrey Tam.

Psaní testů není nijak okouzlující, ale protože díky testům se ze svižné aplikace nestane zmetek plný chyb, je nezbytné. Pokud čtete tento návod, už víte, že byste měli psát testy pro svůj kód a uživatelské rozhraní, ale možná nevíte jak.

Možná máte funkční aplikaci, ale chcete otestovat změny, které provádíte za účelem jejího rozšíření. Možná už máte napsané testy, ale nejste si jisti, zda jsou to ty správné testy. Nebo jste začali pracovat na nové aplikaci a chcete testovat za pochodu.

Tento výukový program vám to ukáže:

  • Jak používat navigátor testů Xcode k testování modelu aplikace a asynchronních metod
  • Jak předstírat interakce s objekty knihovny nebo systému pomocí zásuvných modulů (stubs) a mocks
  • Jak testovat uživatelské rozhraní a výkon
  • Jak používat nástroj pro pokrytí kódu

Po cestě, si osvojíte část slovní zásoby, kterou používají testovací ninjové.

Zjištění, co testovat

Před psaním jakýchkoli testů je důležité znát základy. Co potřebujete testovat?

Jestliže je vaším cílem rozšířit stávající aplikaci, měli byste nejprve napsat testy pro všechny komponenty, které plánujete změnit.

Obecně by testy měly pokrývat:

  • Jádro funkčnosti: Třídy a metody modelu a jejich interakce s kontrolérem
  • Nejčastější pracovní postupy uživatelského rozhraní
  • Ohraničující podmínky
  • Opravy chyb

Nejlepší postupy pro testování

Zkratka FIRST popisuje stručný soubor kritérií pro efektivní jednotkové testy. Tato kritéria jsou:

  • Rychlé:
  • Nezávislé/izolované:
  • Opakovatelné: Při každém spuštění testu byste měli získat stejné výsledky. Externí poskytovatelé dat nebo problémy se souběhem by mohly způsobit občasné selhání.
  • Samopotvrzující: Testy by měly být plně automatizované. Výstup by měl být buď „vyhověl“, nebo „nevyhověl“, a ne spoléhat na interpretaci souboru protokolu programátorem.
  • Včasné:

Při dodržování zásad FIRST budou vaše testy jasné a užitečné, místo aby se změnily v překážky pro vaši aplikaci.

Začínáme

Začněte stažením materiálů k projektu pomocí tlačítka Stáhnout materiály v horní nebo dolní části tohoto výukového kurzu. K dispozici jsou dva samostatné startovací projekty: BullsEye a HalfTunes.

  • BullsEye vychází z ukázkové aplikace v iOS Apprentice. Herní logika se nachází ve třídě BullsEyeGame, kterou si vyzkoušíte během tohoto tutoriálu.
  • HalfTunes je aktualizovaná verze ukázkové aplikace z URLSession Tutorial. Uživatelé se mohou dotazovat na rozhraní API iTunes na skladby a poté stahovat a přehrávat úryvky skladeb.

Jednotkové testování v Xcode

Navigátor Test poskytuje nejjednodušší způsob práce s testy; budete jej používat k vytváření testovacích cílů a spouštění testů proti vaší aplikaci.

Vytvoření cíle jednotkového testu

Otevřete projekt BullsEye a stisknutím klávesy Command-6 otevřete Navigátor testů.

Klikněte na tlačítko + v levém dolním rohu a z nabídky vyberte Nový cíl jednotkového testu…:

Přijměte výchozí název BullsEyeTests. Jakmile se svazek testů objeví v Navigátoru testů, kliknutím na něj otevřete svazek v editoru. Pokud se svazek nezobrazí automaticky, vyřešte problém kliknutím na některý z ostatních navigátorů a poté se vraťte do navigátoru Testy.

Výchozí šablona importuje testovací rámec XCTest a definuje podtřídu BullsEyeTests XCTestCase s setUp(), tearDown() a ukázkovými testovacími metodami.

Testy lze spustit třemi způsoby:

  1. Produkt ▸ Test nebo Příkaz-U.
    1. . Oba tyto způsoby spustí všechny testovací třídy.
    2. Klepněte na tlačítko se šipkou v navigátoru Test.
    3. Klepněte na tlačítko s kosočtvercem ve žlabu.

    Můžete také spustit jednotlivou testovací metodu klepnutím na její kosočtverec, a to buď v navigátoru Testy, nebo ve žlabu.

    Vyzkoušejte si různé způsoby spouštění testů, abyste zjistili, jak dlouho to trvá a jak to vypadá. Ukázkové testy zatím nic nedělají, takže běží opravdu rychle!

    Když všechny testy uspějí, diamanty zezelenají a zobrazí se zaškrtnutí. Kliknutím na šedý diamant na konci testPerformanceExample() otevřete výsledek výkonu:

    Pro tento tutoriál nepotřebujete testPerformanceExample() ani testExample(), takže je odstraňte.

    Použití XCTAssert k testování modelů

    Nejprve použijete funkce XCTAssert k testování základní funkce modelu BullsEye:

    V souboru BullsEyeTests.swift přidejte tento řádek těsně pod příkaz import:

    @testable import BullsEye

    Tím získá jednotkový test přístup k vnitřním typům a funkcím v BullsEye.

    Na začátek třídy BullsEyeTests přidejte tuto vlastnost:

    var sut: BullsEyeGame!

    Tím vytvoříte zástupný znak pro BullsEyeGame, což je testovaný systém (SUT) neboli objekt, kterého se tato třída testovacích případů týká.

    Dále nahraďte obsah setup() tímto:

    super.setUp()sut = BullsEyeGame()sut.startNewGame()

    Tím vytvoříte objekt BullsEyeGame na úrovni třídy, takže všechny testy této testovací třídy mohou přistupovat k vlastnostem a metodám objektu SUT.

    Tady také zavoláte startNewGame(), který inicializuje targetValue. Mnoho testů bude používat targetValue k testování, zda hra správně počítá skóre.

    Než na to zapomenete, uvolněte objekt SUT v tearDown(). Vyměňte jeho obsah za:

    sut = nilsuper.tearDown()
    Poznámka: Je dobrým zvykem vytvořit SUT v setUp() a uvolnit jej v tearDown(), abyste zajistili, že každý test začne s čistým štítem. Další diskusi najdete v příspěvku Jona Reida na toto téma.

    Napsání prvního testu

    Nyní jste připraveni napsat svůj první test!

    Přidejte následující kód na konec BullsEyeTests:

    func testScoreIsComputed() { // 1. given let guess = sut.targetValue + 5 // 2. when sut.check(guess: guess) // 3. then XCTAssertEqual(sut.scoreRound, 95, "Score computed from guess is wrong")}

    Název testovací metody vždy začíná slovem test a následuje popis toho, co testuje.

    Je dobrým zvykem formátovat test na části given, when a pak:

    1. Given: Zde nastavíte všechny potřebné hodnoty. V tomto příkladu vytvoříte hodnotu guess, abyste mohli určit, jak moc se liší od targetValue.
    2. Když: V této části spustíte testovaný kód: check(guess:).
    3. Pak: V této části potvrdíte výsledek, který očekáváte, zprávou, která se vypíše, pokud test selže. V tomto případě by se sut.scoreRound mělo rovnat 95 (100 – 5).

    Spustíte test kliknutím na ikonu kosočtverce ve výpustním řádku nebo v navigátoru Test. Tím se aplikace sestaví a spustí a ikona kosočtverce se změní na zelenou fajfku!

    Poznámka: Chcete-li si prohlédnout úplný seznam tvrzení XCTestAssertions, přejděte na stránku Apple Assertions Listed by Category.

    Debugging a Test

    Záměrně je do BullsEyeGame zabudována chyba, jejíž nalezení si nyní procvičíte. Abyste viděli chybu v akci, vytvoříte test, který v daném úseku odečte 5 od targetValue a vše ostatní ponechá stejné.

    Přidejte následující test:

    func testScoreIsComputedWhenGuessLTTarget() { // 1. given let guess = sut.targetValue - 5 // 2. when sut.check(guess: guess) // 3. then XCTAssertEqual(sut.scoreRound, 95, "Score computed from guess is wrong")}

    Rozdíl mezi guess a targetValue je stále 5, takže skóre by mělo být stále 95.

    V navigátoru bodů přerušení přidejte bod přerušení Test Failure. Ten zastaví běh testu, když testovací metoda zveřejní tvrzení o selhání.

    Spustíte test a ten by se měl zastavit na řádku XCTAssertEqual se selháním testu.

    Podívejte se na sut a guess v ladicí konzoli:

    guess je targetValue - 5, ale scoreRound je 105, nikoli 95!“

    Pro další zkoumání použijte běžný postup ladění: Nastavte bod přerušení na příkaz when a také jeden v BullsEyeGame.swift, uvnitř check(guess:), kde se vytvoří difference. Pak znovu spusťte test a krokováním nad příkazem let difference zkontrolujte hodnotu difference v aplikaci:

    Problém je v tom, že difference je záporná, takže skóre je 100 – (-5). Chcete-li to napravit, měli byste použít absolutní hodnotu difference. V check(guess:) odkomentujte správný řádek a odstraňte ten nesprávný.

    Odstraňte oba body přerušení a znovu spusťte test, abyste potvrdili, že nyní uspěl.

    Použití XCTestExpectation k testování asynchronních operací

    Když jste se naučili testovat modely a ladit selhání testu, je čas přejít k testování asynchronního kódu.

    Otevřete projekt HalfTunes. Ten používá URLSession k dotazování rozhraní iTunes API a stahování ukázek skladeb. Předpokládejme, že jej chcete upravit tak, aby pro síťové operace používal AlamoFire. Abyste zjistili, zda se něco nerozbije, měli byste napsat testy pro síťové operace a spustit je před změnou kódu a po ní.

    URLSession Metody jsou asynchronní: vracejí se hned, ale jejich běh se dokončí až později. Chcete-li testovat asynchronní metody, použijte XCTestExpectation, aby váš test čekal na dokončení asynchronní operace.

    Asynchronní testy jsou obvykle pomalé, takže byste je měli mít oddělené od rychlejších jednotkových testů.

    Vytvořte nový cíl jednotkových testů s názvem HalfTunesSlowTests. Otevřete třídu HalfTunesSlowTests a importujte modul aplikace HalfTunes hned pod stávajícím příkazem import:

    @testable import HalfTunes

    Všechny testy v této třídě používají výchozí URLSession pro odesílání požadavků na servery společnosti Apple, takže deklarujte objekt sut, vytvořte jej v setUp() a uvolněte jej v tearDown().

    Na místo obsahu třídy HalfTunesSlowTests vložte:

    var sut: URLSession!override func setUp() { super.setUp() sut = URLSession(configuration: .default)}override func tearDown() { sut = nil super.tearDown()}

    Dále přidejte tento asynchronní test:

    // Asynchronous test: success fast, failure slowfunc testValidCallToiTunesGetsHTTPStatusCode200() { // given let url = URL(string: "https://itunes.apple.com/search?media=music&entity=song&term=abba") // 1 let promise = expectation(description: "Status code: 200") // when let dataTask = sut.dataTask(with: url!) { data, response, error in // then if let error = error { XCTFail("Error: \(error.localizedDescription)") return } else if let statusCode = (response as? HTTPURLResponse)?.statusCode { if statusCode == 200 { // 2 promise.fulfill() } else { XCTFail("Status code: \(statusCode)") } } } dataTask.resume() // 3 wait(for: , timeout: 5)}

    Tento test kontroluje, zda odeslání platného dotazu do iTunes vrátí stavový kód 200. Většina kódu je stejná jako ta, kterou byste napsali v aplikaci, s těmito dalšími řádky:

    1. expectation(description:): Vrací objekt XCTestExpectation, uložený v promise. Parametr description popisuje, co očekáváte, že se stane.
    2. promise.fulfill(): Toto volání v uzávěru podmínky úspěchu obsluhy dokončení asynchronní metody označí, že očekávání bylo splněno.
    3. wait(for:timeout:): Udržuje test v chodu, dokud nejsou splněna všechna očekávání nebo dokud neskončí interval timeout, podle toho, co nastane dříve.

    Spustit test. Pokud jste připojeni k internetu, měl by test po načtení aplikace v simulátoru trvat asi sekundu.

    Rychlé selhání

    Selhání bolí, ale nemusí trvat věčnost.

    Chcete-li zažít selhání, jednoduše odstraňte „s“ z „itunes“ v adrese URL:

    let url = URL(string: "https://itune.apple.com/search?media=music&entity=song&term=abba")

    Spustit test. Selže, ale trvá celý časový interval! Je to proto, že jste předpokládali, že požadavek bude vždy úspěšný, a proto jste zavolali promise.fulfill(). Protože se požadavek nezdařil, skončil až po vypršení časového limitu.

    Změnou předpokladu to můžete zlepšit a zrychlit selhání testu: Místo čekání na úspěch požadavku čekejte pouze na vyvolání obsluhy dokončení asynchronní metody. K tomu dojde, jakmile aplikace obdrží od serveru odpověď – buď OK, nebo chybovou -, která splní očekávání. Váš test pak může zkontrolovat, zda požadavek uspěl.

    Chcete-li vidět, jak to funguje, vytvořte nový test.

    Nejprve však opravte předchozí test tak, že zrušíte změnu, kterou jste provedli v url.
    Poté přidejte do své třídy následující test:

    func testCallToiTunesCompletes() { // given let url = URL(string: "https://itune.apple.com/search?media=music&entity=song&term=abba") let promise = expectation(description: "Completion handler invoked") var statusCode: Int? var responseError: Error? // when let dataTask = sut.dataTask(with: url!) { data, response, error in statusCode = (response as? HTTPURLResponse)?.statusCode responseError = error promise.fulfill() } dataTask.resume() wait(for: , timeout: 5) // then XCTAssertNil(responseError) XCTAssertEqual(statusCode, 200)}

    Klíčový rozdíl je v tom, že pouhé zadání obsluhy dokončení splní očekávání, a to trvá jen asi sekundu. Pokud požadavek selže, selžou i then tvrzení.

    Spustíte test. Nyní by mělo selhání trvat asi sekundu. Selže proto, že požadavek selhal, nikoli proto, že běh testu překročil timeout.

    Opravte url a pak test spusťte znovu, abyste potvrdili, že nyní uspěl.

    Podvodné objekty a interakce

    Asynchronní testy vám dávají jistotu, že váš kód generuje správný vstup do asynchronního API. Možná budete chtít také otestovat, že váš kód funguje správně, když obdrží vstup z URLSession, nebo že správně aktualizuje výchozí databázi uživatele nebo kontejner iCloudu.

    Většina aplikací interaguje se systémovými nebo knihovními objekty – objekty, které nemáte pod kontrolou – a testy, které interagují s těmito objekty, mohou být pomalé a neopakovatelné, čímž porušují dvě ze zásad FIRST. Místo toho můžete interakce předstírat získáváním vstupů ze záložek nebo aktualizací maketových objektů.

    Použijte předstírání, pokud váš kód závisí na systémovém nebo knihovním objektu. Můžete to udělat tak, že vytvoříte falešný objekt, který bude hrát danou roli, a tento falešný objekt injektujete do svého kódu. Dependency Injection od Jona Reida popisuje několik způsobů, jak to udělat.

    Falešný vstup ze stubu

    V tomto testu zkontrolujete, zda updateSearchResults(_:) aplikace správně analyzuje data stažená relací, a to tak, že ověříte, zda je searchResults.count správný. SUT je řadič zobrazení a vy budete fingovat relaci pomocí stubů a některých předem stažených dat.

    Přejděte do navigátoru Test a přidejte nový cíl testu jednotky. Pojmenujte jej HalfTunesFakeTests. Otevřete HalfTunesFakeTests.swift a importujte modul aplikace HalfTunes hned pod příkazem import:

    @testable import HalfTunes

    Nyní nahraďte obsah třídy HalfTunesFakeTests tímto:

    var sut: SearchViewController!override func setUp() { super.setUp() sut = UIStoryboard(name: "Main", bundle: nil) .instantiateInitialViewController() as? SearchViewController}override func tearDown() { sut = nil super.tearDown()}

    Tímto deklarujete SUT, který je SearchViewController, vytvoříte jej v setUp() a uvolníte v tearDown():

    Poznámka: SUT je řadič zobrazení, protože HalfTunes má obrovský problém s řadičem zobrazení – veškerá práce se provádí v SearchViewController.swift. Přesunutí síťového kódu do samostatného modulu by tento problém omezilo a také by usnadnilo testování.

    Dále budete potřebovat nějaká ukázková data JSON, která vaše falešná relace poskytne testu. Stačí jen několik položek, takže pro omezení výsledků stahování v iTunes přidejte do řetězce URL &limit=3:

    https://itunes.apple.com/search?media=music&entity=song&term=abba&limit=3

    Zkopírujte tuto adresu URL a vložte ji do prohlížeče. Tím se stáhne soubor s názvem 1.txt, 1.txt.js nebo podobně. Prohlédněte si jej a potvrďte, že se jedná o soubor JSON, a pak jej přejmenujte na abbaData.json.

    Nyní se vraťte do Xcode a přejděte do navigátoru Projekt. Přidejte soubor do skupiny HalfTunesFakeTests.

    Projekt HalfTunes obsahuje podpůrný soubor DHURLSessionMock.swift. Ten definuje jednoduchý protokol s názvem DHURLSession s metodami (stubs) pro vytvoření datové úlohy s URL nebo URLRequest. Definuje také URLSessionMock, který odpovídá tomuto protokolu s inicializátory, které umožňují vytvořit maketový objekt URLSession s volbou dat, odpovědi a chyby.

    Pro nastavení makety přejděte do souboru HalfTunesFakeTests.swift a za příkaz, který vytváří SUT, přidejte do setUp() následující:

    let testBundle = Bundle(for: type(of: self))let path = testBundle.path(forResource: "abbaData", ofType: "json")let data = try? Data(contentsOf: URL(fileURLWithPath: path!), options: .alwaysMapped)let url = URL(string: "https://itunes.apple.com/search?media=music&entity=song&term=abba")let urlResponse = HTTPURLResponse( url: url!, statusCode: 200, httpVersion: nil, headerFields: nil)let sessionMock = URLSessionMock(data: data, response: urlResponse, error: nil)sut.defaultSession = sessionMock

    Tím se nastaví falešná data a odpověď a vytvoří se falešný objekt relace. Nakonec na konci vloží falešnou relaci do aplikace jako vlastnost sut.

    Nyní jste připraveni napsat test, který zkontroluje, zda volání updateSearchResults(_:) analyzuje falešná data. Přidejte následující test:

    func test_UpdateSearchResults_ParsesData() { // given let promise = expectation(description: "Status code: 200") // when XCTAssertEqual( sut.searchResults.count, 0, "searchResults should be empty before the data task runs") let url = URL(string: "https://itunes.apple.com/search?media=music&entity=song&term=abba") let dataTask = sut.defaultSession.dataTask(with: url!) { data, response, error in // if HTTP request is successful, call updateSearchResults(_:) // which parses the response data into Tracks if let error = error { print(error.localizedDescription) } else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 { self.sut.updateSearchResults(data) } promise.fulfill() } dataTask.resume() wait(for: , timeout: 5) // then XCTAssertEqual(sut.searchResults.count, 3, "Didn't parse 3 items from fake response")}

    Tento test musíte stále psát jako asynchronní, protože stub se tváří jako asynchronní metoda.

    Tvrzení when je, že searchResults je před spuštěním datové úlohy prázdný. To by měla být pravda, protože jste vytvořili zcela nový SUT v setUp().

    Falešná data obsahují JSON pro tři objekty Track, takže then assertion je, že pole searchResults view controlleru obsahuje tři položky.

    Spustit test. Měl by poměrně rychle uspět, protože neexistuje žádné skutečné síťové připojení!“

    Fake Update to Mock Object

    Předchozí test použil stub pro zajištění vstupu z falešného objektu. Dále použijete maketový objekt k otestování, zda váš kód správně aktualizuje UserDefaults.

    Otevřete znovu projekt BullsEye. Aplikace má dva herní styly: Uživatel buď posune posuvník tak, aby odpovídal cílové hodnotě, nebo cílovou hodnotu odhadne z polohy posuvníku. Segmentový ovládací prvek v pravém dolním rohu přepíná herní styl a ukládá jej do výchozího nastavení uživatele.

    Vaším dalším testem ověříte, zda aplikace správně ukládá vlastnost gameStyle.

    V navigátoru Test klepněte na New Unit Test Class a pojmenujte ji BullsEyeMockTests. Pod příkaz import přidejte následující:

    @testable import BullsEyeclass MockUserDefaults: UserDefaults { var gameStyleChanged = 0 override func set(_ value: Int, forKey defaultName: String) { if defaultName == "gameStyle" { gameStyleChanged += 1 } }}

    MockUserDefaults přepisuje set(_:forKey:) pro inkrementaci příznaku gameStyleChanged. Často se setkáte s podobnými testy, které nastavují proměnnou Bool, ale inkrementace Int vám dává větší flexibilitu – váš test by například mohl kontrolovat, zda je metoda volána pouze jednou.

    Deklarujte SUT a mock objekt v BullsEyeMockTests:

    var sut: ViewController!var mockUserDefaults: MockUserDefaults!

    Dále nahraďte výchozí setUp() a tearDown() tímto:

    override func setUp() { super.setUp() sut = UIStoryboard(name: "Main", bundle: nil) .instantiateInitialViewController() as? ViewController mockUserDefaults = MockUserDefaults(suiteName: "testing") sut.defaults = mockUserDefaults}override func tearDown() { sut = nil mockUserDefaults = nil super.tearDown()}

    Tím vytvoříte SUT a mock objekt a injekujete mock objekt jako vlastnost SUT.

    Nyní nahraďte dvě výchozí testovací metody v šabloně tímto:

    func testGameStyleCanBeChanged() { // given let segmentedControl = UISegmentedControl() // when XCTAssertEqual( mockUserDefaults.gameStyleChanged, 0, "gameStyleChanged should be 0 before sendActions") segmentedControl.addTarget(sut, action: #selector(ViewController.chooseGameStyle(_:)), for: .valueChanged) segmentedControl.sendActions(for: .valueChanged) // then XCTAssertEqual( mockUserDefaults.gameStyleChanged, 1, "gameStyle user default wasn't changed")}

    Tvrzení when je, že příznak gameStyleChanged je 0, než testovací metoda změní segmentované řízení. Pokud je tedy pravdivé i tvrzení then, znamená to, že set(_:forKey:) byla zavolána přesně jednou.

    Spustit test; měl by uspět.

    Testování uživatelského rozhraní v Xcode

    Testování uživatelského rozhraní umožňuje testovat interakce s uživatelským rozhraním. Testování uživatelského rozhraní funguje tak, že se pomocí dotazů vyhledají objekty uživatelského rozhraní aplikace, syntetizují se události a pak se těmto objektům odešlou události. Rozhraní API umožňuje zkoumat vlastnosti a stav objektu uživatelského rozhraní, abyste je mohli porovnat s očekávaným stavem.

    V navigátoru Test projektu BullsEye přidejte nový cíl testování uživatelského rozhraní. Zkontrolujte, zda je Testovaný cíl BullsEye, a pak přijměte výchozí název BullsEyeUITests.

    Otevřete BullsEyeUITests.swift a přidejte tuto vlastnost na začátek třídy BullsEyeUITests:

    var app: XCUIApplication!

    V setUp() nahraďte příkaz XCUIApplication().launch() následujícím:

    app = XCUIApplication()app.launch()

    Změňte název testExample() na testGameStyleSwitch().

    Otevřete nový řádek v testGameStyleSwitch() a klepněte na červené tlačítko Záznam v dolní části okna editoru:

    Tím otevřete aplikaci v simulátoru v režimu, který zaznamenává vaše interakce jako testovací příkazy. Jakmile se aplikace načte, klepněte na segment Posunutí přepínače herního stylu a na horní popisek. Poté klepnutím na tlačítko Xcode Record zastavte nahrávání.

    Nyní máte v testGameStyleSwitch() následující tři řádky:

    let app = XCUIApplication()app.buttons.tap()app.staticTexts.tap()

    Záznamník vytvořil kód pro testování stejných akcí, které jste testovali v aplikaci. Odešlete klepnutí na posuvník a štítek. Ty použijete jako základ pro vytvoření vlastního testu uživatelského rozhraní.
    Pokud uvidíte další příkazy, prostě je odstraňte.

    První řádek duplikuje vlastnost, kterou jste vytvořili v setUp(), takže tento řádek odstraňte. Zatím nemusíte nic ťukat, takže smažte také .tap() na konci řádků 2 a 3. Nyní otevřete malou nabídku vedle a vyberte segmentedControls.buttons.

    To, co vám zůstane, by mělo být následující:

    app.segmentedControls.buttonsapp.staticTexts

    Klepněte na všechny ostatní objekty, aby vám zapisovač pomohl najít kód, ke kterému můžete v testech přistupovat. Nyní nahraďte tyto řádky tímto kódem, abyste vytvořili danou sekci:

    // givenlet slideButton = app.segmentedControls.buttonslet typeButton = app.segmentedControls.buttonslet slideLabel = app.staticTextslet typeLabel = app.staticTexts

    Teď, když máte názvy pro dvě tlačítka v segmentovaném ovládacím prvku a dva možné horní popisky, přidejte níže následující kód:

    // thenif slideButton.isSelected { XCTAssertTrue(slideLabel.exists) XCTAssertFalse(typeLabel.exists) typeButton.tap() XCTAssertTrue(typeLabel.exists) XCTAssertFalse(slideLabel.exists)} else if typeButton.isSelected { XCTAssertTrue(typeLabel.exists) XCTAssertFalse(slideLabel.exists) slideButton.tap() XCTAssertTrue(slideLabel.exists) XCTAssertFalse(typeLabel.exists)}

    Tento kód kontroluje, zda existuje správný popisek, když tap() na každé tlačítko v segmentovaném ovládacím prvku. Spusťte test – všechna tvrzení by měla být úspěšná.

    Testování výkonu

    Z dokumentace společnosti Apple: Výkonnostní test vezme blok kódu, který chcete vyhodnotit, desetkrát ho spustí a shromáždí průměrnou dobu provádění a směrodatnou odchylku pro jednotlivá spuštění. Zprůměrováním těchto jednotlivých měření se vytvoří hodnota běhu testu, kterou pak lze porovnat s výchozí hodnotou a vyhodnotit úspěch či neúspěch.

    Napsat test výkonu je velmi jednoduché:

    Chcete-li to vidět v akci, znovu otevřete projekt HalfTunes a v poli HalfTunesFakeTests.swift přidejte následující test:

    func test_StartDownload_Performance() { let track = Track( name: "Waterloo", artist: "ABBA", previewUrl: "http://a821.phobos.apple.com/us/r30/Music/d7/ba/ce/mzm.vsyjlsff.aac.p.m4a") measure { self.sut.startDownload(track) }}

    Spuštěte test a poté klikněte na ikonu, která se zobrazí vedle začátku koncového uzávěru measure(), abyste viděli statistiku.

    Kliknutím na Set Baseline nastavte referenční čas. Poté znovu spusťte test výkonu a prohlédněte si výsledek – může být lepší nebo horší než výchozí hodnota. Tlačítko Upravit umožňuje obnovit výchozí hodnotu na tento nový výsledek.

    Výchozí hodnoty se ukládají podle konfigurace zařízení, takže můžete mít stejný test spuštěný na několika různých zařízeních a na každém z nich udržovat jinou výchozí hodnotu závislou na rychlosti procesoru, paměti atd.

    Pokud v aplikaci provedete změny, které by mohly mít vliv na výkon testované metody, spusťte test výkonu znovu, abyste zjistili, jak se srovnává s výchozí hodnotou.

    Pokrytí kódu

    Nástroj pro pokrytí kódu vám řekne, jaký kód aplikace je skutečně spouštěn vašimi testy, takže víte, které části kódu aplikace nejsou (zatím) testovány.

    Chcete-li zapnout pokrytí kódu, upravte akci Testy schématu a na kartě Možnosti zaškrtněte políčko Shromáždit pokrytí pro:

    Spustit všechny testy (Příkaz-U), poté otevřete navigátor Sestava (Příkaz-9). V tomto seznamu vyberte pod horní položkou možnost Pokrytí:

    Klikněte na odhalovací trojúhelník a zobrazte seznam funkcí a uzávěrů v souboru SearchViewController.swift:

    Přejděte dolů na updateSearchResults(_:) a uvidíte, že pokrytí je 87,9 %.

    Kliknutím na tlačítko se šipkou u této funkce otevřete zdrojový soubor k této funkci. Při přejetí myší přes anotace pokrytí v pravém postranním panelu se úseky kódu zvýrazní zeleně nebo červeně:

    Anotace pokrytí ukazují, kolikrát test zasáhl jednotlivé úseky kódu; úseky, které nebyly volány, jsou zvýrazněny červeně. Jak se dalo očekávat, smyčka for proběhla třikrát, ale nic v chybových cestách se neprovedlo.

    Chcete-li zvýšit pokrytí této funkce, můžete duplikovat soubor abbaData.json a pak jej upravit tak, aby způsoboval různé chyby. Například změňte "results" na "result" pro test, který narazí na print("Results key not found in dictionary").

    100% pokrytí?

    Jak moc byste měli usilovat o 100% pokrytí kódu? Zadejte do Googlu „100% pokrytí jednotkových testů“ a najdete řadu argumentů pro i proti, spolu s debatami o samotné definici „100% pokrytí“. Argumenty proti říkají, že posledních 10-15 % nestojí za námahu. Argumenty pro říkají, že posledních 10-15 % je nejdůležitějších, protože je tak těžké je otestovat. Zadejte si do Googlu „hard to unit test bad design“ a najděte přesvědčivé argumenty, že netestovatelný kód je známkou hlubších problémů s návrhem.

    Kam dál?

    Máte nyní několik skvělých nástrojů, které můžete použít při psaní testů pro své projekty. Doufám, že vám tento návod na testování jednotek a uživatelského rozhraní systému iOS dodal sebedůvěru, abyste mohli vše otestovat!

    Plnou verzi projektu si můžete stáhnout pomocí tlačítka Stáhnout materiály v horní nebo dolní části tohoto návodu. Pokračujte v rozvíjení svých dovedností přidáním dalších vlastních testů.

    Nabízíme vám několik zdrojů pro další studium:

    • Na WWDC existuje několik videí na téma testování. Dvě dobrá z konference WWDC17 jsou následující:
    • Dalším krokem je automatizace: Engineering for Testability a Testing Tips & Tricks: Continuous Integration a Continuous Delivery. Začněte článkem Automatizace procesu testování pomocí serveru Xcode a xcodebuild od společnosti Apple a článkem o kontinuálním dodávání na Wikipedii, který čerpá ze zkušeností společnosti ThoughtWorks.
    • Pokud už máte aplikaci, ale ještě jste pro ni nenapsali testy, můžete se podívat na knihu Working Effectively with Legacy Code od Michaela Featherse, protože kód bez testů je kódem starým!“
    • Archivy ukázkových aplikací Quality Coding od Jona Reida jsou skvělé pro získání dalších informací o vývoji řízeném testy.

    týdeník raywenderlich.com

    Zpravodaj raywenderlich.com je nejjednodušší způsob, jak zůstat v obraze o všem, co jako mobilní vývojář potřebujete vědět.

    Získejte týdenní přehled našich tutoriálů a kurzů a jako bonus získejte zdarma podrobný kurz e-mailem!

    Průměrné hodnocení

    4,7/5

    Přidejte hodnocení tohoto obsahu

    Pro přidání hodnocení se přihlaste

    90 hodnocení

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.