swift test unit (IV) test framework constituents using Quick + Nimble

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:

c中, kiwi  , look , cropped

swift:quick+nimbleSleipnir

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 arrangephase 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))

参考链接:

使用swift给objc项目做单元测试

【CodeTest】TDD,BDD及初步使用Quick

iOS测试与集成

Swift 进阶开发指南:如何使用 Quick、Nimble 执行测试驱动开发(TDD)

Guess you like

Origin blog.csdn.net/lin1109221208/article/details/93048812