国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

Core Data by tutorials 筆記(六)

2018-02-24 15:55 更新

原文出處: http://chengway.in/post/ji-zhu/core-data-by-tutorials-bi-ji-liu

今天我們來(lái)關(guān)注一下CoreData的單元測(cè)試,其實(shí)在寫(xiě)程序之前,先寫(xiě)測(cè)試,將問(wèn)題一點(diǎn)點(diǎn)分解,也是TDD所倡導(dǎo)的做法,這也是我今年所期望達(dá)成的一個(gè)目標(biāo),新開(kāi)項(xiàng)目按TDD的流程來(lái)做,以后也會(huì)整理些這方面的東西。如果你對(duì)CoreData的其他方面感興趣請(qǐng)查看我之前的筆記或直接購(gòu)買(mǎi)《Core Data by Tutorials》

Chapter 8: Unit Testing

作者列舉了一系列單元測(cè)試的好處:幫助你在一開(kāi)始就組織好項(xiàng)目的結(jié)構(gòu),可以不用操心UI去測(cè)試核心功能。單元測(cè)試還可以方便重構(gòu)。還可以更好地拆分UI進(jìn)行測(cè)試。

這章主要焦距在XCTest這個(gè)框架來(lái)測(cè)試Core Data程序,多數(shù)情況下Core Data的測(cè)試要依賴(lài)于真實(shí)的Core Data stack,但又不想將單元測(cè)試的test data與你手動(dòng)添加的接口測(cè)試弄混,本章也提供了解決方案。

一、Getting started

本章要測(cè)試的是一個(gè)關(guān)于野營(yíng)管理的APP,主要管理營(yíng)地、預(yù)訂(包括時(shí)間表和付款情況)。作者將整個(gè)業(yè)務(wù)流程分解為三塊:

  1. campsites(野營(yíng)地)
  2. campers(野營(yíng)者)
  3. reservations(預(yù)訂)

由于swift內(nèi)部的訪(fǎng)問(wèn)控制,app和其test分別屬于不同的targets和不同的modules,因此你并不能普通地從tests中訪(fǎng)問(wèn)app中的classes,這里有兩個(gè)解決辦法:

  1. 把App中的classes和methods標(biāo)記為public,是其對(duì)tests可見(jiàn)。
  2. 直接在test target里添加所需要的classes。

作者提供的實(shí)例中,已經(jīng)將要測(cè)試的類(lèi)和方法標(biāo)記為public的了,現(xiàn)在就可以對(duì)Core Data部分進(jìn)行測(cè)試了,作者在測(cè)試開(kāi)始前給了一些建議:

Good unit tests follow the acronym?FIRST:

??Fast: If your tests take too long to run, you won’t bother running them.

??Isolated: Any test should function properly when run on its own or before or after any other test.

??Repeatable: You should get the same results every time you run the test against the same codebase.

??Self-verifying: The test itself should report success or failure; you shouldn’t have to check the contents of a file or a console log.

??Timely: There’s some benefit to writing the tests after you’ve already written the code, particularly if you’re writing a new test to cover a new bug. Ideally, though, the tests come first to act as a specification for the functionality you’re developing.

為了達(dá)到上面提到“FIRST”目標(biāo),我們需要修改Core Data stack使用in-memory store而不是SQLite-backed store。具體的做法是為test target創(chuàng)建一個(gè)CoreDataStack的子類(lèi)來(lái)修改store type。

class TestCoreDataStack: CoreDataStack { 
    override init() {
        super.init() 
        self.persistentStoreCoordinator = {
            var psc: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel:
                self.managedObjectModel) 
            var error: NSError? = nil
            var ps = psc!.addPersistentStoreWithType( 
                NSInMemoryStoreType, configuration: nil, 
                URL: nil, options: nil, error: &error)
            if (ps == nil) { 
                abort()
            }
            return psc
        }()
    } 
}

二、Your first test

單元測(cè)試需要將APP的邏輯拆分出來(lái),我們創(chuàng)建一個(gè)類(lèi)來(lái)封裝這些邏輯。作者這里創(chuàng)建的第一個(gè)測(cè)試類(lèi)為CamperServiceTestsXCTestCase的子類(lèi),用來(lái)測(cè)試APPCamperService類(lèi)中的邏輯

import UIKit
import XCTest
import CoreData
import CampgroundManager
//
class CamperServiceTests: XCTestCase {
  var coreDataStack: CoreDataStack!
  var camperService: CamperService!
    override func setUp() {
        super.setUp()
        coreDataStack = TestCoreDataStack()
        camperService = CamperService(managedObjectContext: coreDataStack.mainContext!, coreDataStack: coreDataStack)
    }
    override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
        coreDataStack = nil
        camperService = nil
    }
    func testAddCamper() {
        let camper = camperService.addCamper("Bacon Lover", phoneNumber: "910-543-9000")
        XCTAssertNotNil(camper, "Camper should not nil")
        XCTAssertTrue(camper?.fullName == "Bacon Lover")
        XCTAssertTrue(camper?.phoneNumber == "910-543-9000")
    }

setUp會(huì)在每次測(cè)試前被調(diào)用,這里可以創(chuàng)建一些測(cè)試需要用到東西,而且因?yàn)槭褂玫氖莍n-memory store,每次在setUp中創(chuàng)建的context都是全新的。tearDown相對(duì)于setUp,是在每次test結(jié)束后調(diào)用,用來(lái)清除一些屬性。上面的例子主要測(cè)試了addCamper()方法。

這里注意的就是該測(cè)試創(chuàng)建的對(duì)象和屬性都不會(huì)保存在任何store中的。

三、Asynchronous tests

關(guān)于異步測(cè)試,這里用到了兩個(gè)context,一個(gè)root context運(yùn)行在后臺(tái)線(xiàn)程中,另外一個(gè)main context是root context的子類(lèi),讓context分別在正確的線(xiàn)程中執(zhí)行其實(shí)也很簡(jiǎn)單,主要采用下面兩種方法:

  1. performBlockAndWait() 將等待block里的內(nèi)容執(zhí)行完后才繼續(xù)
  2. performBlock() 執(zhí)行到此方法立即返回

測(cè)試第二種performBlock()方法時(shí)可能會(huì)需要些技巧,因?yàn)閿?shù)據(jù)可能不會(huì)立即得到,還好XCTestCase提供了一個(gè)叫expectations的新特性。下面展示了使用expectation來(lái)完成對(duì)異步方法的測(cè)試:

let expectation = self.expectationWithDescription("Done!");
someService.callMethodWithCompletionHandler() { 
    expectation.fulfill()
}
self.waitForExpectationsWithTimeout(2.0, handler: nil)

該特性的關(guān)鍵是要么是expectation.fulfill()被執(zhí)行,要么觸發(fā)超時(shí)產(chǎn)生一個(gè)異常expectation,這樣test才能繼續(xù)。

我們現(xiàn)在來(lái)為CamperServiceTests繼續(xù)增加一個(gè)新的方法來(lái)測(cè)試root context的保存:

func testRootContextIsSavedAfterAddingCamper() { 
//1 創(chuàng)建了一個(gè)針對(duì)異步測(cè)試的方法,主要是通過(guò)觀(guān)察save方法觸發(fā)的通知,觸發(fā)通知后具體的handle返回一個(gè)true。
    let expectRoot = self.expectationForNotification( 
        NSManagedObjectContextDidSaveNotification, 
        object: coreDataStack.rootContext) {
            notification in 
            return true
    }
//2 增加一個(gè)camper
    let camper = camperService.addCamper("Bacon Lover", 
        phoneNumber: "910-543-9000")
//3 等待2秒,如果第1步?jīng)]有return true,那么就觸發(fā)error
    self.waitForExpectationsWithTimeout(2.0) { 
        error in
        XCTAssertNil(error, "Save did not occur") 
    }
}

四、Tests first

這一節(jié)新建了一個(gè)CampSiteServiceTests?Class 對(duì)CampSiteService進(jìn)行測(cè)試,具體code形式與上一節(jié)類(lèi)似,添加了測(cè)試testAddCampSite()testRootContextIsSavedAfterAddingCampsite(),作者在這里主要展示了TDD的概念。

Test-Driven Development (TDD) is a way of developing an application by writing a test first, then incrementally implementing the feature until the test passes. The code is then refactored for the next feature or improvement.

根據(jù)需求又寫(xiě)了一個(gè)testGetCampSiteWithMatchingSiteNumber()方法用來(lái)測(cè)試getCampSite(),因?yàn)閏ampSiteService.addCampSite()方法在之前的測(cè)試方法中已經(jīng)通過(guò)測(cè)試了,所以這里可以放心去用,這就是TDD的一個(gè)精髓吧。

func testGetCampSiteWithMatchingSiteNumber(){
    campSiteService.addCampSite(1, electricity: true,
        water: true)
let campSite = campSiteService.getCampSite(1) 
    XCTAssertNotNil(campSite, "A campsite should be returned")
}
func testGetCampSiteNoMatchingSiteNumber(){ 
    campSiteService.addCampSite(1, electricity: true,
        water: true)
    let campSite = campSiteService.getCampSite(2)
    XCTAssertNil(campSite, "No campsite should be returned") 
}

寫(xiě)完測(cè)試方法運(yùn)行一下CMD+U,當(dāng)然通不過(guò)啦,我們還沒(méi)有實(shí)現(xiàn)他?,F(xiàn)在為CampSiteService類(lèi)添加一個(gè)getCampSite()方法:

public func getCampSite(siteNumber: NSNumber) -> CampSite? { 
    let fetchRequest = NSFetchRequest(entityName: "CampSite")   fetchRequest.predicate = NSPredicate(
        format: "siteNumber == %@", argumentArray: [siteNumber]) 
    var error: NSError?
    let results = self.managedObjectContext.executeFetchRequest(
        fetchRequest, error: &error)
    if error != nil || results == nil { 
        return nil
    }
    return results!.first as CampSite? 
}

現(xiàn)在重新CMD+U一下,就通過(guò)了。

五、Validation and refactoring

最后一節(jié)主要針對(duì)APP中的ReservationService類(lèi)進(jìn)行測(cè)試,同樣的是創(chuàng)建一個(gè)ReservationServiceTests測(cè)試類(lèi),這個(gè)test類(lèi)的setUP和tearDown與第三節(jié)類(lèi)似。只不過(guò)多了campSiteService與camperService的設(shè)置。在testReserveCampSitePositiveNumberOfDays()方法中對(duì)ReservationService類(lèi)里的reserveCampSite()進(jìn)行測(cè)試后,發(fā)現(xiàn)沒(méi)有對(duì)numberOfNights有效性進(jìn)行判斷,隨后進(jìn)行了修改,這也算是展示了單元測(cè)試的另一種能力。作者是這么解釋的:不管你對(duì)這些要測(cè)試的code有何了解,你盡肯能地針對(duì)這些API寫(xiě)一些測(cè)試,如果OK,那么皆大歡喜,如果出問(wèn)題了,那意味著要么改進(jìn)code要么改進(jìn)測(cè)試代碼。

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)