IOS Apps 开发(Swift)(7)——Define Your Data Model

前言:网上一直没有找到用Swift开发IOS的好的教程,所以找了官网的文档翻译一下算了。如有错误欢迎指正。博主首发CSDN,mcf171专栏。

博客链接:mcf171的博客

原文链接:Define Your Data Model

——————————————————————————————

在本次课程中,我们将定义和测试 FoodTracker 的数据模型。一个数据模型是用来展现anpp的信息结构。

学习目标

在本次课程中,你可以了解到:

  • 创建一个数据模型
  • 在自定义类中创建一个可失败的初始化器
  • 理解可失败和不可失败的初始化器
  • 通过运行单元测试来检测数据模型

创建数据模型

现在我们要创建一个数据模型来存储我们的菜品场景信息。因此我们需要定义一个简单的类,包括名字、照片和打分

创建一个新的数据模型类

1、选择 File > New > File (or press Command-N).

2、在左边的对话框选择iOS下的Source 

3、选择Swift文件,点击下一步

我们之前创建RatingControl类的时候是使用不同的步骤(iOS > Source > Cocoa Touch Class),这是因为现在我们是创建一个基类,不需要从其他类继承。

4、输入名字Meal

5、选择保存位置同时勾选测试


6、点击Create

Xcode将创建一个Meal.swift的文件

在Swift中,我们能通过字符串来保存名字,使用UIImage保存图片,使用Int保存打分。因为图片不一定有,所以我们对于UIImage使用Optional类型

定义菜品的数据类型

1、返回标准编辑框



2、打开Meal.swift

3、修改import

import UIKit

默认情况下Swift文件引入的是 Foundation 框架,因此我们可以使用 Foundation 数据结构。 因为我们接下来是会用来自UIKit 框架的类,所以我们需要将UIKit引入到我们的代码中。同时引入 UIKit 也允许我们访问Foundation中的数据,所以我们可以所见为一行代码。

4、在import语句下写如下代码

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

代码定义了基本的属性值。

5、在上述代码下,添加初始化器

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

初始化器是在一个类进行实例化的时候调用的方法。

6、在初始化器中加入以下代码

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

如果没有使用正确的值来创建Meal会发生什么呢?比如说一个空的名字或者一个负数分数。我们需要返回一个nil来表明这个对象不能被创建。并且设置默认值。

7、在初始化器的最后我们添加 if 语句来检测是否合法

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

因为初始化器现在可能返回 nil ,所以我们需要表明在初始化器表明

8、点击错误提示,选择 fix-it来修补错误


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

可失败的初始化器的声明形式就是如上所示,也就是意味初始化器可能返回空值。

到目前为止,我们的初始化器应该如下:

// 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
    }
}

里程碑:选择 Product > Build (or pressing Command-B)编译工程。虽然我们还没有使用这个类,但是可以通过编译来检测是否我们有语法上的错误。

测试数据

尽管我们的数据模型已经建立了,但是我们还没有将这个数据模型整合到我们的App中。因此很难说我们就百分百的正确完成了这个数据模型。我们可能会在运行时碰到一些我们没有考虑到的边界情况。

查看FoodTracker的单元测试文件

1、打开 FoodTrackerTests文件夹。展开它


2、打开FoodTrackerTest.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.
        }
    }
    
}

XCTest 框架是 Xcode的测试框架。单元测试本身也是一个类。FoodTrackerTests继承了XCTestCase。注释解释了了setUp和tearDown方法的用处


我们要写的主要代码是功能测试——检测是否能产生我们期望的值、和性能测试——我们的代码能否像我们期望的那样运行。因为我们没有写过任何性能密集型的代码,所以我们只需要测试功能性。

针对菜品对象初始化器写单元测试

1、在FoodTrackerTests.swift中删除测试模板

import UIKit
import XCTest
 
class FoodTrackerTests: XCTestCase {
    
}

在本次课程中我们不需要任何模板

2、在最后一个花括号增加如下注释

// MARK: FoodTracker Tests

3、在注视下增加单元测试方法

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

4、首先增加一个能通过的测试用例。

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

XCTAssertNotNil用来测试Meal对象是否在初始化后为nil,也就是意味着初始化器成功的创建了一个Meal对象。

5、现在增加一个失败的测试用例

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

XCTAssertNIl 则断言一个对象是nil。在这个测试用例中对象是nil因为我们的没有输出名字。

6、现在增加一个测试用例,这个测试用里会返回一个nil的对象,但是我们断言对象不是nil

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

最终我们的testMealInitialization方法为:

// 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)
    XCTAssertNotNil(badRating)
}

我们可以通过Command + U 来运行我们的单元测试。或者我们可以运行一个单独的测试。最后一个测试用例会报错,因为我们知道他是nil但是断言为非nil

运行 testMealInitialzation单元测试

1、打开FoodTrackerTests.swift 找到testMealInitialization

2、在测试方法的左边找到一个菱形


3、将我们的鼠标移动到菱形块上,从而变成一个小的运行按钮


4、点击这个按钮

里程碑:当app运行单元测试的时候,前面两个测试用例会通过,最后一个会失败


正如我们看到的,单元测试可以帮助我们捕捉代码的错误地方,如果我们期望在最后的测试用例中Meal对象不是nil,那么我们能够在测试中捕捉错误。

修复测试用例

1、找到FoodTrackerTests.swift文件中的testMealInitialization方法

2、将最后一行改为

XCTAssertNil(badRating, "Negative ratings are invalid, be positive")
testMealInitialization方法最后为:

// 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")
}
里程碑:再次运行测试用例的时候,所有测试用例都通过了


测试用例是我们代码中必要的一部分,因为他可以帮厨我们捕捉到我们可能忽视的错误。每个测试都需要检测特殊的,基本的行为。付过我们写的测试用例是非常长和复杂的,那么非常难追踪到发生错误的准确位置

ps:本课程工程文件Download File












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值