48
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

Quickのmatcherとして使われているNimbleはとても柔軟に書くことができます。
NimbleのREADMEも充実しているので、そんなに躓くことなく書くことができるのですが、

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

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

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

Comparisons

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

関数 記号 意味
beLessThan < actualがexpectより小さかったらテストが通ります。
beLessThanOrEqualTo <= actualがexpectより小さいか同じだったらテストが通ります。
beGreaterThan > actualがexpectより大きかったらテストが通ります。
beGreaterThanOrEqualTo >= actualがexpectより大きいか同じだったらテストが通ります。
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にする必要があります。

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

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

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になってしまいました。

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

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

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

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

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

Truthiness

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

関数 意味
beTruthy nilではないか、trueの場合はテストが通ります。
beTrue trueの時のみテストが通ります。
beFalsy nilであるか、falseの場合はテストが通ります。
beFalse falseの時のみテストが通ります。
beNil nilの場合にテストが通ります。
let bl:Bool? = true
expect(bl).to(beTruthy())
expect(bl).to(beTrue())

let str:String? = "hoge"
expect(str).to(beTruthy())
let bl:Bool? = false
expect(bl).to(beFalsy())
expect(bl).to(beFalse())

let str:String? = nil
expect(str).to(beFalsy())
let bl:Bool? = nil
expect(bl).to(beNil())

Swift Error Handling

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

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

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

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

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

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

Verify collection count

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

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))
48
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?