環境
- Xcode12.1
- Swift 5.3
Nimble とは
Use Nimble to express the expected outcomes of Swift or Objective-C expressions. Inspired by Cedar.
https://github.com/Quick/Nimble#nimble
Swift (や Objective-C) のユニットテストで使うOSSで、評価と期待値を簡単に書けるようなっています。
テストフレームワークのQuick 内のツールのようですが単体でも使用できます。
現在の最新バージョン v9.0.0 のライセンスはこちら。
https://github.com/Quick/Nimble/blob/v9.0.0/LICENSE
こんな感じで書ける
// XCTest の書き方
XCTAssertTrue(hoge)
// Nimble の書き方
expect(hoge).to(beTrue())
ざっくり言うと expect(評価対象).to(期待値) という書き方になります。
メリットとデメリット
個人的には主に以下のようなメリデメがあると考えています。
- メリット
- こけたときの情報が分かりやすく、原因の特定が容易
- 非同期処理のテストを簡単に書ける
- 英語の文法に近いので何をしているか読みやすい
- デメリット
- OSSなのでいつまでサポートされるか分からない
- 学習コストが発生する(ただし非常に小さいと思う)
こけたときの情報が分かりやすい

上図のように、素の書き方の方は実際値が nil でこけたのか false でこけたのかが分かりません。
一方Nimble の方は期待値とともに実際の値も示してくれるので、
原因の特定が非常に容易になります。
非同期処理のテストを簡単に書ける
class Hoge {
var isDone = false
func async() {
DispatchQueue.main.async { [self] in
isDone = true
}
}
}
例えば上記クラスのテストとして、
「async メソッドを呼ぶと、isDone が true になること」を担保したいとき。
素の書き方だと以下のようになると思います。
func testExample() throws {
let hoge = Hoge()
hoge.async()
let exp = expectation(description: "Async")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
XCTAssertTrue(hoge.isDone)
exp.fulfill()
}
wait(for: [exp], timeout: 1.0)
}
ぱっと見でもう辛い。
(もっといい書き方があれば教えていただきたいです🙏)
一方、Nimble だと以下のように一行で書けます。
func testExample() throws {
let hoge = Hoge()
hoge.async()
expect(hoge.isDone).toEventually(beTrue())
}
一行。
非同期処理のテストが全然怖くなくなります。
個人的には前向きに検討してもよいくらいメリットがあるのかなと感じています。
特にTDDで開発する場合、テストを書く→実装する→リファクタする、
をいかにテンポよく書けるかで開発がノってくるかが左右されます。
そんなときにテスト記述でつまずいていたら開発に全くノれないですね。
その点Nimble はテスト記述で迷うことが少ないので、
その課題をうまく解消できるのではと思っています。
ということで、以降は導入とメソッドの一部をさらっと紹介します。
Nimble をインストール
Swift Package Manager でインストールしてみます。
Qiita に iOSアプリ開発にSwift Package Managerを使おう という記事があるので、その通りに進めます。
パッケージのリポジトリのURLは https://github.com/Quick/Nimble を入力します。
また、テスト時のみ使用すると思うので、テストターゲットを指定でよいです。

以上です!
Nimble の使い方
詳しくは Nimble の README や Qiita: Nimbleの使い方サンプルを作ってみた
を見てもらえるとよいと思います。
ここではざっくり程度に書くので雰囲気を掴んでいただければ。
// AAA が BBB であること
expect(AAA).to(equal(BBB))
// AAA が True であること
expect(AAA).to(beTrue())
// AAA が True ではないこと
expect(AAA).toNot(beTrue())
or
expect(AAA).notTo(beTrue())
// AAA が非同期でTrue になること(デフォルトでは1秒待ちます: GitHub の該当箇所)
expect(AAA).toEventually(beTrue())
// AAA が非同期でTrue 以外になること
expect(AAA).toEventuallyNot(beTrue())
or
expect(AAA).toNotEventually(beTrue())
// AAA が1以上であること
expect(AAA).to(beGreaterThanOrEqualTo(1))
// AAA が Hoge クラスのインスタンスであること
expect(AAA).to(beAnInstanceOf(Hoge.self))
// AAA が Hoge クラスもしくはその子クラスのインスタンスであること
expect(AAA).to(beAKindOf(Hoge.self))
// AAA が1.0のプラスマイナス0.2の範囲内の値であること
expect(AAA).to(beCloseTo(1.0, within: 0.2))
最後に
楽しくTDDをしたいですね!
以上です。お疲れ様でした!