Nimbleの使い方サンプルを作ってみた

Quickのmatcherとして使われているNimbleはとても柔軟に書くことができます。
[NimbleのREADME](https://github.com/Quick/Nimble)も充実しているので、そんなに躓くことなく書くことができるのですが、

* でもやっぱりちょっと躓いたところがある
* すぐに動かせるサンプルがあった方が楽

だと思ったのでサンプルを作ってみました。
基本的には上記2項目に該当するもののサンプルを書いているので、これでNimbleの全てがわかるわけではありません。
https://github.com/akatsuki174/QuickSample
(以前投稿した記事のサンプルも混じっていますが、いずれにしてもQuick, Nimbleのサンプルになっています。あとでリファクタリング、多少のサンプルの追加はしようと思っています。)

この記事はそのサンプルのポイント解説という位置づけで書いていきます。

# [Comparisons](https://github.com/Quick/Nimble#comparisons)

数値など大小の比較ができるものに対して使うものになります。

|関数|記号|意味|
|:--|:--|:--|
| beLessThan |<|actualがexpectより小さかったらテストが通ります。|
| beLessThanOrEqualTo |<=|actualがexpectより小さいか同じだったらテストが通ります。|
| beGreaterThan |>|actualがexpectより大きかったらテストが通ります。|
| beGreaterThanOrEqualTo |>=|actualがexpectより大きいか同じだったらテストが通ります。|

```swift
let one = 1
let five = 5

expect(one).to(beLessThan(five))
expect(one) < five
                
expect(one).to(beLessThanOrEqualTo(five))
expect(one) <= five
                
expect(five).to(beGreaterThan(one))
expect(five) > one
                
expect(five).to(beGreaterThanOrEqualTo(one))
expect(five) >= one
```

上記は整数値の場合は何も問題なく比較することができます。
しかし浮動小数点数の等値比較は必ずしも成功するわけではありません。そのため「多少誤差があっても同じ数値だとみなし」てテストすることもできます。
そのような場合は`beCloseTo`や`≈`を使います。
許容する誤差は下記withinの部分やdeltaの部分に書きます。
ちなみにこの時Floatではダメで、Doubleにする必要があります。

```swift
let five:Double = 5
let five_point_one:Double = 5.1

expect(five_point_one).to(beCloseTo(five, within: 0.2))
expect(5.0001) ≈ five // デフォルトのdeltaの0.0001が適用される
let delta = 0.2
expect(five_point_one) ≈ (five, delta)
expect(five_point_one) ≈ five ± delta
expect(five_point_one) == five ± delta
```

また、同じようにして配列の要素もテストすることができます。

```swift
expect([0.00001, 2.00001]) ≈ [0.0, 2.0]
expect([0.1, 2.1]).to(beCloseTo([0.0, 2.0], within: 0.2))
```

これに関しては一度公式に書かれているコード(以下)をそのまま貼り付けてテストしてみたのですが、test failedになってしまいました。

```swift
expect([0.0, 2.0]) ≈ [0.0001, 2.0001]
expect([0.0, 2.0]).to(beCloseTo([0.1, 2.1], within: 0.1))
```

デフォルトのdeltaの0.0001以内に入っているので通過しそうではあるのですが。。。
結局、私のサンプルのようにdeltaを少し大きくしたら無事テスト通過しました。
これに関してはもうちょっと調べてみたいと思っています。

# [Types/Classes](https://github.com/Quick/Nimble#typesclasses)

|関数|意味|
|:--|:--|
| beAnInstanceOf |あるインスタンスがあるクラスのインスタンスであった場合はテストが通ります。|
| beAKindOf |あるインスタンスがあるクラスのインスタンスである、もしくはそのクラスのサブクラスのインスタンスであった場合はテストが通ります。|

これらの関数を使う場合、インスタンスはObjective-Cのオブジェクトである必要があります。なので、NSObjectを継承するか、`@objc`プレフィックスをつける必要があります。

```swift
expect(animal).to(beAnInstanceOf(Animal))
expect(cat).to(beAKindOf(Animal))
```

※AnimalはNSObjectを継承、CatはAnimalを継承。

# [Truthiness](https://github.com/Quick/Nimble#truthiness)

ある値の真偽、もしくは値が入っているかどうかを検証する関数になります。

|関数|意味|
|:--|:--|
|beTruthy|nilではないか、trueの場合はテストが通ります。|
|beTrue|trueの時のみテストが通ります。|
|beFalsy|nilであるか、falseの場合はテストが通ります。|
|beFalse|falseの時のみテストが通ります。|
|beNil|nilの場合にテストが通ります。|

```swift
let bl:Bool? = true
expect(bl).to(beTruthy())
expect(bl).to(beTrue())

let str:String? = "hoge"
expect(str).to(beTruthy())
```

```swift
let bl:Bool? = false
expect(bl).to(beFalsy())
expect(bl).to(beFalse())

let str:String? = nil
expect(str).to(beFalsy())
```

```swift
let bl:Bool? = nil
expect(bl).to(beNil())
```

# [Swift Error Handling](https://github.com/Quick/Nimble#swift-error-handling)

`throwError`を使えば正しくエラーハンドリングが行われているかどうかのテストも作ることができます。

```swift
let str = ""
expect{ try self.printStr(str) }.to(throwError())
                    
expect{ try self.printStr(str) }.to(throwError { (error: ErrorType) in
  expect(error._domain).to(equal("the value is empty"))
  expect(error._code).to(equal(-1))
})
                    
expect{ try self.requestLogin(str) }.to(throwError(RequestError.Unknown))
expect{ try self.requestLogin(str) }.to(throwError(errorType: RequestError.self))


func printStr(str: String) throws {
  if str.isEmpty {
    throw NSError(domain: "the value is empty", code: -1, userInfo: nil)
  } else {
    print(str)
  }
}

func requestLogin(name: String) throws {
  if name.isEmpty {
    throw RequestError.Unknown
  }
  // request processing...
}
    
enum RequestError: ErrorType {
  case Unknown
}
```

# [Collection Membership](https://github.com/Quick/Nimble#collection-membership)

|関数|意味|
|:--|:--|
|contain|actualにexpectの要素が含まれていた場合はテストが通ります。|
|beEmpty|actualが空だった場合にテストが通ります。|
|beginWith|actualの要素の最初がexpectの要素だった場合はテストが通ります。|
|endWith|actualの要素の最後がexpectの要素だった場合はテストが通ります。|

```swift
let arr:[String] = ["Swift", "Objective-C", "Python"]
expect(arr).to(contain("Swift", "Objective-C"))
expect(arr).to(beginWith("Swift"))
expect(arr).to(endWith("Python"))

let emptyArr:[String] = []
expect(emptyArr).to(beEmpty())
```

# [Strings](https://github.com/Quick/Nimble#strings)

|関数|意味|
|:--|:--|
|contain|actualにexpectの文字列が含まれていた場合はテストが通ります。|
|beginWith|actualがexpectの文字列から始まっていた場合にテストが通ります。|
| endWith |actualがexpectの文字列で終わった場合はテストが通ります。|
| match |actualの文字列がexpectの正規表現に合致している場合はテストが通ります。|

```swift
let str = "My favorite language is Swift."
expect(str).to(contain("Swift"))
expect(str).to(beginWith("My"))
expect(str).to(endWith("."))
expect(str).to(match("My*"))

let emptyStr = ""
expect(emptyStr).to(beEmpty())
```

# [Checking if all elements of a collection pass a condition](https://github.com/Quick/Nimble#checking-if-all-elements-of-a-collection-pass-a-condition)

ArrayやSetなどのSequenceTypeの場合、`allPass`を用いてその中の要素全てが一定の条件を満たしているかどうかを調べることができます。

```swift
let arr = [1, 3, 5, 7]
let set = Set<Int>(arrayLiteral: 1, 3, 5, 7)

expect(arr).to(allPass({$0 < 10}))
expect(arr).to(allPass(beLessThan(10)))
                    
expect(set).to(allPass({$0 < 10}))
expect(set).to(allPass(beLessThan(10)))
```

ちなみにテストに失敗した時は以下のような表示になります。
![スクリーンショット 2016-03-13 21.03.09.png](https://qiita-image-store.s3.amazonaws.com/0/100736/36183c73-ef10-9e2a-b47b-16c28ca94495.png)

# [Verify collection count](https://github.com/Quick/Nimble#verify-collection-count)

ArrayやSet、DictionaryなどのCollectionTypeの場合、`haveCount`を用いて要素数に関するテストを行うことができます。

```swift
let arr = [1, 3, 5, 7]
let set = Set<Int>(arrayLiteral: 1, 3, 5, 7)

expect(arr).to(haveCount(4))
expect(set).notTo(haveCount(5))
```