<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                > 原文出處: http://chengway.in/post/ji-zhu/core-data-by-tutorials-bi-ji-liu 今天我們來關注一下CoreData的單元測試,其實在寫程序之前,先寫測試,將問題一點點分解,也是TDD所倡導的做法,這也是我今年所期望達成的一個目標,新開項目按TDD的流程來做,以后也會整理些這方面的東西。如果你對CoreData的其他方面感興趣請查看我之前的筆記或直接購買[《Core Data by Tutorials》](http://www.raywenderlich.com/store/core-data-by-tutorials) ## **Chapter 8: Unit Testing** 作者列舉了一系列單元測試的好處:幫助你在一開始就組織好項目的結構,可以不用操心UI去測試核心功能。單元測試還可以方便重構。還可以更好地拆分UI進行測試。 這章主要焦距在XCTest這個框架來測試Core Data程序,多數情況下Core Data的測試要依賴于真實的Core Data stack,但又不想將單元測試的test data與你手動添加的接口測試弄混,本章也提供了解決方案。 ### **一、Getting started** 本章要測試的是一個關于野營管理的APP,主要管理營地、預訂(包括時間表和付款情況)。作者將整個業務流程分解為三塊: 1. campsites(野營地) 2. campers(野營者) 3. reservations(預訂) 由于swift內部的訪問控制,app和其test分別屬于不同的targets和不同的modules,因此你并不能普通地從tests中訪問app中的classes,這里有兩個解決辦法: 1. 把App中的classes和methods標記為**public**,是其對tests可見。 2. 直接在test target里添加所需要的classes。 作者提供的實例中,已經將要測試的類和方法標記為*public*的了,現在就可以對Core Data部分進行測試了,作者在測試開始前給了一些建議: > Good unit tests follow the acronym?**FIRST**: > > ??**F**ast: If your tests take too long to run, you won’t bother running them. > > ??**I**solated: Any test should function properly when run on its own or before or after any other test. > > ??**R**epeatable: You should get the same results every time you run the test against the same codebase. > > ??**S**elf-verifying: The test itself should report success or failure; you shouldn’t have to check the contents of a file or a console log. > > ??**T**imely: 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. 為了達到上面提到“**FIRST**”目標,我們需要修改Core Data stack使用**in-memory store**而不是SQLite-backed store。具體的做法是為**test target**創建一個CoreDataStack的子類來修改*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** 單元測試需要將APP的邏輯拆分出來,我們創建一個類來封裝這些邏輯。作者這里創建的第一個測試類為**CamperServiceTests**是**XCTestCase**的子類,用來測試APP**CamperService**類中的邏輯 ~~~ 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**會在每次測試前被調用,這里可以創建一些測試需要用到東西,而且因為使用的是in-memory store,每次在setUp中創建的context都是全新的。**tearDown**相對于*setUp*,是在每次test結束后調用,用來清除一些屬性。上面的例子主要測試了addCamper()方法。 ~~~ 這里注意的就是該測試創建的對象和屬性都不會保存在任何store中的。 ~~~ ### **三、Asynchronous tests** 關于異步測試,這里用到了兩個context,一個root context運行在后臺線程中,另外一個main context是root context的子類,讓context分別在正確的線程中執行其實也很簡單,主要采用下面兩種方法: 1. performBlockAndWait() 將等待block里的內容執行完后才繼續 2. performBlock() 執行到此方法立即返回 測試第二種performBlock()方法時可能會需要些技巧,因為數據可能不會立即得到,還好XCTestCase提供了一個叫**expectations**的新特性。下面展示了使用**expectation**來完成對異步方法的測試: ~~~ let expectation = self.expectationWithDescription("Done!"); someService.callMethodWithCompletionHandler() { expectation.fulfill() } self.waitForExpectationsWithTimeout(2.0, handler: nil) ~~~ 該特性的關鍵是要么是*expectation.fulfill()*被執行,要么觸發超時產生一個異常expectation,這樣test才能繼續。 我們現在來為CamperServiceTests繼續增加一個新的方法來測試root context的保存: ~~~ func testRootContextIsSavedAfterAddingCamper() { //1 創建了一個針對異步測試的方法,主要是通過觀察save方法觸發的通知,觸發通知后具體的handle返回一個true。 let expectRoot = self.expectationForNotification( NSManagedObjectContextDidSaveNotification, object: coreDataStack.rootContext) { notification in return true } //2 增加一個camper let camper = camperService.addCamper("Bacon Lover", phoneNumber: "910-543-9000") //3 等待2秒,如果第1步沒有return true,那么就觸發error self.waitForExpectationsWithTimeout(2.0) { error in XCTAssertNil(error, "Save did not occur") } } ~~~ ### **四、Tests first** 這一節新建了一個**CampSiteServiceTests**?Class 對CampSiteService進行測試,具體code形式與上一節類似,添加了測試**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. 根據需求又寫了一個**testGetCampSiteWithMatchingSiteNumber()**方法用來測試**getCampSite()**,因為campSiteService.addCampSite()方法在之前的測試方法中已經通過測試了,所以這里可以放心去用,這就是TDD的一個精髓吧。 ~~~ 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") } ~~~ 寫完測試方法運行一下CMD+U,當然通不過啦,我們還沒有實現他。現在為CampSiteService類添加一個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? } ~~~ 現在重新CMD+U一下,就通過了。 ### **五、Validation and refactoring** 最后一節主要針對APP中的**ReservationService**類進行測試,同樣的是創建一個**ReservationServiceTests**測試類,這個test類的setUP和tearDown與第三節類似。只不過多了campSiteService與camperService的設置。在*testReserveCampSitePositiveNumberOfDays()*方法中對ReservationService類里的**reserveCampSite()**進行測試后,發現沒有對numberOfNights有效性進行判斷,隨后進行了修改,這也算是展示了單元測試的另一種能力。作者是這么解釋的:不管你對這些要測試的code有何了解,你盡肯能地針對這些API寫一些測試,如果OK,那么皆大歡喜,如果出問題了,那意味著要么改進code要么改進測試代碼。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看