定義你的數據模型

在本課程中,您將定義和測試的應用程序FoodTracker數據模型。一個數據模型表示在APP中的的信息結構。swift

學習目標數據結構

在課程結束時,你將可以:app

1.建立數據模型
2.寫failable初始化一個自定義類
3.證實failable和nonfailable的不一樣,理解他們之間的差別和概念
4.經過編寫和運行單元測試來測試數據模型框架

建立一個數據模型ide

如今你須要建立一個數據模型來存儲菜譜場景所須要顯示的信息。要作到這一點,咱們須要定義個簡單的類,裏面有name,photo,rating函數

建立一個新的數據模型類性能

1.選擇File > New > File (或按下Command+N)單元測試

2在對話框左邊,選擇iOS下的Source學習

3.選擇Swift File,而後點擊下一步測試

和前面建立RatingControl 所不一樣,對於數據模型,咱們不須要繼承其餘類

4.在Save as旁,鍵入Meal

5.默認保存位置是項目目錄,Group默認選擇爲你的App 名字FoodTracker,在Target選擇,確保app和tests都被選中

 

6.點擊Create。Xocde會建立一個名叫Meal.swift的文件

在Swift中,你能使用一個String表示一個name,使用UIImage表示photo,使用Int表示rating。由於一個菜譜老是會有一個name和rating。但不必定會有圖片。因此UIImage是可選的

爲菜譜定義數據模型

1.打開Meal.swift

2.修改導入語句爲UIKit

import UIKit

默認1個Swift文件會導入爲Foundation框架,它使你可以使用基礎數據結構。你須要使用UIKit框架中的類,因此你須要導入UIKit,同時UIKit也能讓你訪問Foundation,所以你能夠移除這個多餘的Foundation語句

3.而後添加以下代碼:

class Meal {
    // MARK: Properties
    
    var name: String
    var photo: UIImage?
    var rating: Int
}

代碼定義了你須要存儲的基礎屬性。你使用變量(var)而不是常量(let),由於他們在整個菜譜的生命週期中會發生改變

4.在屬性代碼下方,添加以下初始化代碼:

// MARK: Initialization
 
init(name: String, photo: UIImage?, rating: Int) {
}

回憶一個初始化方法,當一個類的實例準備初始化時,咱們能夠爲每一個屬性設置一個初始化值,或執行一些設置和初始化操做

5.經過參數值來給基礎屬性賦值

// Initialize stored properties.
self.name = name
self.photo = photo
self.rating = rating

若是你嘗試使用不正確的值來建立一個菜譜,會發生如一個空的name或負數的rating。你須要返回nil來表示item不能被建立,也不能設置爲默認值。你須要添加代碼來檢查一些失敗的狀況

6.在初始化結束的地方,加入if語句來檢查無效值

// Initialization should fail if there is no name or if the rating is negative.
if name.isEmpty || rating < 0 {
    return nil
}

由於初始化函數中可能返回nil,因此你須要在初始化函數指出標識

7.點擊fix-it,在init關鍵字後面來添加(?)

init?(name: String, photo: UIImage?, rating: Int) {

這個初始化程序被稱爲failable initializer,它表示這個初始化程序可能會返回nil

如今總體init函數,應該以下所示:

// MARK: Initialization
 
init?(name: String, photo: UIImage?, rating: Int) {
    // Initialize stored properties.
    self.name = name
    self.photo = photo
    self.rating = rating
    
    // Initialization should fail if there is no name or if the rating is negative.
    if name.isEmpty || rating < 0 {
        return nil
    }
}

測試你的數據

雖然你的數據模型編譯了,但你尚未徹底將其歸入到你的APP中。所以,很難判斷是否已正確實現一切,在運行時你可能會遇到,沒有考慮的邊緣狀況。

爲了解決這種不肯定性,你能夠寫單元測試,單元測試用於測試小的,獨立的代碼片斷,以確保他們的行爲正確。菜譜類正好是單元測試一個完美的候選人。Xcode已經幫你建立了一個單元測試文件

查看FoodTracker中的單元測試中的文件

1.打開FoodTrackerTests文件夾,經過點擊三角形,展開裏面的列表

2.打開 FoodTrackerTests.swift

花一點時間來了解文件中的代碼

 

import UIKit
import XCTest
 
class FoodTrackerTests: XCTestCase {
    
    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }
    
    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }
    
    func testExample() {
        // This is an example of a functional test case.
        XCTAssert(true, "Pass")
    }
    
    func testPerformanceExample() {
        // This is an example of a performance test case.
        self.measureBlock() {
            // Put the code you want to measure the time of here.
        }
    }
    
}

 

咱們導入的XCText框架,是Xcode的測試框架。單元測試會定義在FoodTrackerTests類中,它繼承自XCTestCase,代碼註釋解釋了setUp()tearDown() 方法

你寫的功能測試(用來檢查一切產生的值是否符合你的預期)和性能測試(檢查你代碼的性能是否是符合你預期那樣快)是主要的測試類型。由於你沒有寫任何耗性能的代碼,因此這樣你只要寫功能測試便可。

通常在咱們的測試方法前,會加上text前綴,如咱們先要測試init方法,會這麼寫testMealInitialization

爲菜譜對象的初始化函數編寫單元測試

1.在 FoodTrackerTests.swift中,刪除測試模版

import UIKit
import XCTest
 
class FoodTrackerTests: XCTestCase {
    
}

本例中,咱們不使用任何模版代碼

2.在類中,添加以下方法

// Tests to confirm that the Meal initializer returns when no name or a negative rating is provided.
func testMealInitialization() {
}

添加註釋是一個好習慣,能夠幫助你或其餘瀏覽你代碼的人,看懂你這個方法的意思,是要作什麼

3.首先添加一個經過的測試用例。添加註釋

// Success case.
let potentialItem = Meal(name: "Newest meal", photo: nil, rating: 5)
XCTAssertNotNil(potentialItem)

XCTAssertNotNil是測試Meal對象在初始化後不爲nil,這意味這你提供的參數會在初始化程序中成功建立一個Meal對象。

4.如今添加一個Meal失敗的測試用例。添加以下代碼:

// Failure cases.
let noName = Meal(name: "", photo: nil, rating: 0)
XCTAssertNil(noName, "Empty name is invalid")

XCTAssertNil斷言這個對象是nil。本例中,意思是noName這個對象爲nil,意味着它初始化失敗。你指望這個初始化失敗,由於這個名字是一個空字符串,你明確地針對初始程序測試。

5.咱們在添加一個測試失敗的用例,但此次咱們斷言他會成功:

let badRating = Meal(name: "Really bad rating", photo: nil, rating: -1)
XCTAssertNotNil(badRating)

你指望這個用例會失敗,由於rating爲負數

您能夠經過按下Command-U在運行單元測試。最後一個測試用例預期失敗,由於你斷言對象非nil,它其實是nil。

運行單元測試

1.在FoodTrackerTests.swift中,找到testMealInitialization()單元測試

2.在測試名稱的左邊,找到一個菱形

 

3.將鼠標懸停在菱形上會出現一個小的運行按鈕

4.點擊這個執行按鈕來運行單元測試

檢查站:你的應用程序運行在你剛剛編寫的單元測試上。前兩個測試用例應該經過,最後應該失敗。

 

正如你所看到的,單元測試有助於捕獲代碼中的錯誤。若是你真的但願在最後一個測試中的對象是非nil,你會在測試過程當中發現這個錯誤。(在這種狀況下,是由於你故意寫了一個失敗的測試案例,你能夠回頭去解決你的測試案例)

修復測試用例

1.在FoodTrackerTests.swift中找到testMealInitialization()

2.修改代碼爲

 

XCTAssertNil(badRating, "Negative ratings are invalid, be positive")

 

完整的方法看起來應該是這樣:

// Tests to confirm that the Meal initializer returns when no name or a negative rating is provided.
func testMealInitialization() {
    // Success case.
    let potentialItem = Meal(name: "Newest meal", photo: nil, rating: 5)
    XCTAssertNotNil(potentialItem)
    
    // Failure cases.
    let noName = Meal(name: "", photo: nil, rating: 0)
    XCTAssertNil(noName, "Empty name is invalid")
    
    let badRating = Meal(name: "Really bad rating", photo: nil, rating: -1)
    XCTAssertNil(badRating, "Negative ratings are invalid, be positive")
}

檢查站:運行你剛剛編寫單元測試。全部的測試用例應該會經過。

相關文章
相關標籤/搜索