使用Xcode 7进行单元测试

7 篇文章 0 订阅

单元测试简介

单元测试是指对项目工程中每一个小的模块来单独进行测试,这些模块可以指一个方法、一个类或者一系列的类组成的一个功能模块。

单元测试的目的就是验证这些模块是否按照预想的逻辑去执行。只有每个模块都能正常运作,最后的应用程序运行时才不会出错。

编写单元测试能够提前发现模块中存在的问题并及时解决,如果不进行单元测试,而是在App所有代码完成时直接看运行效果,这个时候可能会有多个模块同时存在各种各样的问题,同时来解决这些问题肯定会有非常大的难度,所以应尽量避免出现这种情况。下面我们就来介绍怎么使用 XCTest框架来进行单元测试

使用 XCTest 框架进行单元测试

在最新的 Xcode 7 中,Apple 为我们提供了XCTest 框架来进行测试,它不仅可以用来进行单元测试,还可以配合模拟器进行 UI 相关的测试。这里我们主要介绍单元测试的用法,UI 测试就不涉及了。

为工程添加测试支持

如果是新的工程,在创建的时候选中Inclue Unit Tests,Xcode 会自动为我们创建测试需要的样板代码;
这里写图片描述
如果是已有工程的话, 可以在Project info界面按图中的方式给工程添加测试支持:
这里写图片描述
然后工程中会多出一个ProjectNameTests的group,并且有一个已经创建好的XCTest的类,下面是这个类的结构:

import XCTest

class UnitTestDemoTests: 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.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
    }

    func testPerformanceExample() {
        // This is an example of a performance test case.
        self.measureBlock {
            // Put the code you want to measure the time of here.
        }
    }
}

这是一个继承自XCTestCase 的类,一般单元测试所要用的功能都在这个类里,下面介绍一下这几个方法的含义:

  • testExample - 这是一个自动生成的测试用例方法,每一个测试用例都需要以test开头,后面可以跟上测试的具体内容,在每个test方法所在的行号旁边,都有一个菱形的图标,点击它就可以运行相应的测试用例。同样,在当前类上也有一个这样的图标,点击的话会运行该类里所有的测试用例。
    这里写图片描述
  • setUp - 这是一个用来进行初始化的方法,运行每一个测试用例之前,都会先运行这个方法,所以一般会把一些重复的代码放到这里来初始化。

  • tearDown - 这个方法是每个测试用例运行完成后执行的,可以用来进行释放资源等操作

  • testPerformanceExample - 也是一个测试用例,它内部通过一个 self.measureBlock 闭包来测试代码的性能。把要测试的代码写到这个闭包里,就可以得出这段代码的运行时间。

使用XCTAssert***编写测试用例

上面介绍了单元测试的基本配置,接下来我们来看一下怎么编写测试用例来测试我们的代码。

XCTest 框架为我们定义了以 XCTAssert 开头的断言,可以很方便的来测试代码的运行结果,下面简单介绍几个

  • XCTAssertTrue(expression: BooleanType) - 这个方法用来判断expression是否为true
  • XCTAssertNil(expression: Any?) - 这个方法用来判断expression 是否为nil
  • XCTAssertEqual(expression1: T?, expression2: T?) - 用来判断 两个表达式是否相等,这个方法还有很多不同泛型的重载,可以用来接收不同的参数类型,如集合类型,字典类型等。
  • XCTAssertGreaterThan(expression1: T, expression2: T) - 用来判断expression1是否大于expression2

上面列举的只是一小部分,类似的断言方法还有很多,就不一一列举了。另外对于这些方法,都会有一个带message: String 参数的重载,这个参数可以用来在assert fail 时输出错误信息,以快速定位出错的地方。

下面就通过一个例子来介绍怎么使用这些断言。

首先来看一下要测试的代码内容:

// PeopleGenerator.swift
class PeopleGenerator: NSObject {

    func people(withName name: String, age: Int) -> People? {
        return People(name: name, age: age)
    }
}
// People.swift
class People: NSObject {
    var name: String
    var age: Int

    init?(name: String, age: Int) {
        if age < 0 {
            return nil
        }

        self.name = name
        self.age = age
        super.init()
    }
}

在 PeopleGenerator里声明了一个方法people(withName: age:) 这个方法来创建一个 People的实例,通过People类里的构造器可以看出去,在age < 0 的时候可失败构造器会返回nil,这刚好也符合通常的逻辑。

下面我们就针对people(withName: age:) 这个方法来编写测试用例,测试代码是否会按照我们预定的逻辑去执行

import XCTest
@testable import UnitTestDemo   // 首先需要import 当前的项目 UnitTestDemo 是当前的工程名,并用 @testable 标记

class UnitTestDemoTests: XCTestCase {

    var generator: PeopleGenerator!

    override func setUp() {
        super.setUp()
        generator = PeopleGenerator() // setUp里实例化 generator 对象
    }

    override func tearDown() {
        super.tearDown()
        generator = nil  // tearDown 里释放资源 
    }
    // 测试用例
    func testPeopleGenerate() {
        let age = -1
        let people = generator.people(withName: "name", age: age)
        XCTAssertNil(people, "people is not nil")
    }

    func testPerformanceExample() {

        self.measureBlock {

        }
    }

}

按照我们代码的逻辑,当 age = -1 的时候,People 类的构造器就会失败,因此我们的测试用例是可以通过测试的:

testSucceed

测试通过后,函数名左边的菱形图标会变成绿色的,如果没有通过,则会变成红色,下面我们再把age 变成 1,再来运行一下测试用例:

testFailed

不出所料,这次测试是没有通过的,而且还会标识出那一条断言是没有通过的,如果这里写了多条断言,就可以知道是哪一条出错了,同时我们传递的message参数也会出现在错误提示里。这还是很方便的。

测试异步调用

上面讲的这种测试方法只适用于同步调用的情况,当要测试的方法是一个异步的调用,例如一个网络请求,那么用上面的方法就不行了。例如我们用下面的方法来测试网络请求是否正常(注意配置App Transport Security)。

func testAsyncRequest() {

        NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://www.baid.com")!, completionHandler: { (data, respoonse, error) in
            XCTAssertNotNil(data)
            XCTAssertNotNil(error)
        }).resume()

    }

直接这么写的话是也是可以通过的,这是因为还没等到completionHandler 里的XCTAssert运行,testAsyncRequest 方法就直接返回了,所以Xcode 会直接判断当前的测试为通过。但实际情况并不是这样,因为我故意把百度的网址拼错了。

针对这种情况,就需要用到 XCTExpectation 这个类了。我们通过它来给测试的运行加上一定的条件,调用 XCTExpectation 的fulfill 方法来添加条件,只有满足了所有的fulfill 或者timeOut 超时,测试方法才可以返回。下面来看具体用法

func testAsyncRequest() {
        let expectation = self.expectationWithDescription("http request")

        NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://www.baidu.com")!, completionHandler: { (data, respoonse, error) in
            XCTAssertNotNil(data)
            XCTAssertNotNil(error)

            expectation.fulfill() // 满足了fulfill 条件或 timeOut 才可以返回
        }).resume()

        self.waitForExpectationsWithTimeout(1, handler: nil) // 等待
    }

现在把网址改成正确的,再次运行程序,就会发现这次测试会失败,因为我们同时对data 和 error进行了 AssertNotNil,他们中必定有一个不为nil:

testAsyncFailed

但这也说明我们的测试用例写对了,然后去掉 XCAssertNotNil(error) 就可以测试通过了。

到这里XCTest 的单元测试的基本用法就基本讲完了;最后再说一点,有时候Xcode会莫名其妙的不会出现 test 方法前的菱形按钮,这时候可以通过将左边的Navigator 窗口切换到第五个按钮那里,这里显示的就是可以测试的用例,在这里来运行测试也是可以的。

最后来运行一下 class 类里的所有测试用例来作为结束吧:

testClass

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值