1, Apple's official test framework XCTest advantages and disadvantages
Advantages: with Xcode integration of depth, a dedicated Test navigation bar.
Disadvantages:
1 ) because of the limited official test API , thus function is not very rich.
2 ) not very good in writing and readability. In too many test cases, because of the various test methods are fragmented, want to find a particular test in a long test file and thoroughly understand what this test is not a very easy thing to do.
3 ) All tests were done by the assertion, and very often assert meaning not particularly clear, for the delivery of the project or when new developers to join, often takes a lot to understand or conversion costs. In addition, description of each test have been written after the assertion, inclusion in the code, it is difficult to find.
4 ) Use XCTest test Another problem is difficult to mock or stub
Why use Quick + Nimble?
Mainly due to Apple's official test methods and assertions framework is not clear, readability bad, we hardly need to spend a lot of time transfer projects, it is recommended to test the tripartite framework
Mainstream tripartite testing framework are:
Because the project is swift use of language, so the main use of Quick + Nimble , for unit tests and assertions.
If your project is OC, we recommend Kiwi , currently is the largest start tripartite framework.
2, Quick + Nimble Introduction
Quick is a built XCTest on for Swift and Objective-C testing framework design . Test using Swift to write the App very friendly for Swift users who, Quick is the best choice.
It is through DSL to write is very similar to RSpec test cases.
Nimble like Quick partner, which provides matching as assertions, matching mode for writing.
3, Quick + Nimble use
1) Configuration Quick + Nimble
(1) using mounting cocopod Quick + Nimble, pod description file following
platform :ios, '9.0'
use_frameworks!
def testing_pods
pod 'Quick', '~>1.0.0'
pod 'Nimble', '~>5.0.0'
end
target 'testTests' do
testing_pods
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '3.2'
end
end
end
(2) performed in the terminal pod install
(3) before use, xcode required in the project build settting defines module set to yes
(4) In the build phases -> link binary with libraries introduced into the frame
2) using the Quick + Nimble
(1) into the frame test file
import Quick
import Nimble
(2) under test import file into the target item to be tested
//这一行基本上就是标示出我们正在测试的项目目标,然后允许我们从那里 import classes
@testable import test
(3) will inherit the type of test files into QuickSpec , must ensure that our class is a subclass of QuickSpec, it also had XCTestCase subclasses.
There is only one spec method, all tests are on this method
import Quick
import Nimble
@testable import test
class BananaTests: QuickSpec {
override func spec(){
//所有测试放在这里
}
}
(4) Test writing format
Create a Dolphin class for testing
public struct Click{
public var isLoud = true
public var hasHighFrequency = true
public func count()->Int{
return 1
}
}
class Dolphin {
public var isFriendly = true
public var isSmart = true
public var isHappy = false
public init(){
}
public init(_ happy : Bool){
isHappy = happy
}
public func click()->Click{
return Click()
}
public func eat(_ food : AnyObject){
isHappy = true
}
}
The test format is the format i.e. three axes given - when - then / arrange - act - assert
The first to use Apple's official XCTest
func testA_Dolphin_its_click_whenTheDolphinIsNearSomethingInteresting(){
//given / arrange
let dolphin : Dolphin = Dolphin()
//when / act
let click = dolphin.click()
//then / assert
XCTAssertEqual(click.count(), 3)
}
func testA_Dolphin_its_click_whenTheDolphinIsNotNearAnythingInteresting(){
//given / arrange
let dolphin : Dolphin = Dolphin()
//when / act
let click = dolphin.click()
//then / assert
XCTAssertEqual(click.count(), 1)
}
Then replaced nimble frame assertion
func testA_Dolphin_its_click_whenTheDolphinIsNearSomethingInteresting(){
//given / arrange
let dolphin : Dolphin = Dolphin()
//when / act
let click = dolphin.click()
//then / assert
// XCTAssertEqual(click.count(), 3)
expect(click.count()).to(equal(3))
}
func testA_Dolphin_its_click_whenTheDolphinIsNotNearAnythingInteresting(){
//given / arrange
let dolphin : Dolphin = Dolphin()
//when / act
let click = dolphin.click()
//then / assert
// XCTAssertEqual(click.count(), 1)
expect(click.count()).to(equal(1))
}
Finally, using quick + nimble in contrast to the official
import Quick
import Nimble
@testable import test
class DolphinQuickTests: QuickSpec {
override func spec(){
//所有测试放在这里
// describe用于描述类和方法
describe("a dolphin", closure: {
var dolphin : Dolphin!
// beforeEach/afterEach相当于setUp/tearDown,beforeSuite/afterSuite相当于全局setUp/tearDown
beforeEach {
dolphin = Dolphin()
}
describe("its click", closure: {
var click : Click!
beforeEach {
click = dolphin.click()
}
// context用于指定条件或状态
context("when the dolphin is not near anything interesting", closure: {
// it用于描述测试的方法名
it("it only emited once", closure: {
expect(click.count()).to(equal(1))
})
})
context("when the dolphin is near something interesting", closure: {
it("it emited three times", closure: {
expect(click.count()).to(equal(3))
})
})
})
})
}
}
Contrast conclusion:
1) no longer in the same frame as the official Quick, the method name from the particularly long
2 ) Nimble there is more concise , closer to natural language syntax ,
3 ) Quick allows us to write more tests descriptive , and , to simplify our code , especially the arrange
phase of the code .
4, Quick Keyword Description
Keyword | use |
describe | The method described in classes and |
context | Specifies the condition or state |
it | The name used to describe the test method |
beforeEach/afterEach | Equivalent setUp / tearDown |
beforeSuite/afterSuite | Equivalent to the global setUp / teardown |
在describe 、context、it前加“x” | 表示可以屏蔽此方法的测试 |
在describe 、context、it前加“f” | 表示可以只测试这些带f的测试 |
5、Nimble关键字说明
Nimble一般使用 expect(...).to
和 expect(...).notTo
的写法
1)支持异步测试
dispatch_async(dispatch_get_main_queue()) {
ocean.add("dolphins")
ocean.add("whales")
}
expect(ocean).toEventually(contain("dolphins"), timeout: 3)
2)使用waitUntil来进行等待
waitUntil { done in
// do some stuff that takes a while...
NSThread.sleepForTimeInterval(0.5)
done()
}
3)列举Nimble中的匹配函数
用途 | 函数 |
等值判断 | 使用equal函数 expect(actual).to(equal(expected)) expect(actual) == expected expect(actual) != expected |
是否是同一个对象 |
使用beIdenticalTo函数 expect(actual).to(beIdenticalTo(expected)) expect(actual) === expected expect(actual) !== expected |
比较 | expect(actual).to(beLessThan(expected)) expect(actual) < expected
expect(actual).to(beLessThanOrEqualTo(expected)) expect(actual) <= expected
expect(actual).to(beGreaterThan(expected)) expect(actual) > expected
expect(actual).to(beGreaterThanOrEqualTo(expected)) expect(actual) >= expected |
比较浮点数 | expect(10.01).to(beCloseTo(10, within: 0.1)) |
类型检查 |
expect(instance).to(beAnInstanceOf(aClass)) expect(instance).to(beAKindOf(aClass)) |
是否为真 |
// Passes if actual is not nil, true, or an object with a boolean value of true: expect(actual).to(beTruthy())
// Passes if actual is only true (not nil or an object conforming to BooleanType true): expect(actual).to(beTrue())
// Passes if actual is nil, false, or an object with a boolean value of false: expect(actual).to(beFalsy())
// Passes if actual is only false (not nil or an object conforming to BooleanType false): expect(actual).to(beFalse())
// Passes if actual is nil: expect(actual).to(beNil()) |
是否有异常 |
// Passes if actual, when evaluated, raises an exception: expect(actual).to(raiseException())
// Passes if actual raises an exception with the given name: expect(actual).to(raiseException(named: name))
// Passes if actual raises an exception with the given name and reason: expect(actual).to(raiseException(named: name, reason: reason))
// Passes if actual raises an exception and it passes expectations in the block // (in this case, if name begins with 'a r') expect { exception.raise() }.to(raiseException { (exception: NSException) in expect(exception.name).to(beginWith("a r")) }) |
集合关系 |
// Passes if all of the expected values are members of actual: expect(actual).to(contain(expected...)) expect(["whale", "dolphin", "starfish"]).to(contain("dolphin", "starfish"))
// Passes if actual is an empty collection (it contains no elements): expect(actual).to(beEmpty())
|
字符串 |
// Passes if actual contains substring expected: expect(actual).to(contain(expected))
// Passes if actual begins with substring: expect(actual).to(beginWith(expected))
// Passes if actual ends with substring: expect(actual).to(endWith(expected))
// Passes if actual is an empty string, "": expect(actual).to(beEmpty())
// Passes if actual matches the regular expression defined in expected: expect(actual).to(match(expected)) |
检查集合中的所有元素是否符合条件 |
// with a custom function: expect([1,2,3,4]).to(allPass({$0 < 5}))
// with another matcher: expect([1,2,3,4]).to(allPass(beLessThan(5))) |
检查集合个数 |
expect(actual).to(haveCount(expected)) |
匹配任意一种检查 |
// passes if actual is either less than 10 or greater than 20 expect(actual).to(satisfyAnyOf(beLessThan(10), beGreaterThan(20)))
// can include any number of matchers -- the following will pass expect(6).to(satisfyAnyOf(equal(2), equal(3), equal(4), equal(5), equal(6), equal(7)))
// in Swift you also have the option to use the || operator to achieve a similar function expect(82).to(beLessThan(50) || beGreaterThan(80)) |
参考链接: