使用Xcode 7进行单元测试

Posted on Posted in iOS, Swift

单元测试简介

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

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

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

使用 XCTest 框架进行单元测试

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

为工程添加测试支持

如果是新的工程,在创建的时候选中Inclue Unit Tests,Xcode 会自动为我们创建测试需要的样板代码;

project
如果是已有工程的话, 可以在Project info界面按图中的方式给工程添加测试支持:

project2
然后工程中会多出一个ProjectNameTests的group,并且有一个已经创建好的XCTest的类,下面是这个类的结构:

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

  • testExample – 这是一个自动生成的测试用例方法,每一个测试用例都需要以test开头,后面可以跟上测试的具体内容,在每个test方法所在的行号旁边,都有一个菱形的图标,点击它就可以运行相应的测试用例。同样,在当前类上也有一个这样的图标,点击的话会运行该类里所有的测试用例。

testFunction

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

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

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

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

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

测试异步调用

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

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

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

 

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

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

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

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

testClass

发表评论

电子邮件地址不会被公开。 必填项已用*标注