在本課程中,您將定義和測試的應用程序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") }
檢查站:運行你剛剛編寫單元測試。全部的測試用例應該會經過。