Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
33
Help us understand the problem. What is going on with this article?

More than 5 years have passed since last update.

@akatsuki174

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

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))
33
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
33
Help us understand the problem. What is going on with this article?