仕事でQuickを使ってユニットテストを書いてみてなかなかいいなと思ったので、XCTestと比較しつつQuickについて説明しようと思います。
ここで使用しているコードは以下のGitHubから抜粋しています。下記GitではRealm周りのテストをXCTest, Quickを使って書いています。
XCTestに関係なくQuickとNimbleでこんなことできるよ的なサンプルはこれから随時追加していく予定です。
akatsuki174/QuickSample
XCTestとは
皆さんご存知の通り、Xcode5で導入されたユニットテストフレームワークです。
Quickとは
Swift, Objective-CのためのBDD(ビヘイビア駆動開発)テストフレームワークです。
RSpec, Specta, Ginkgoの影響を受けています。
最近日本語ドキュメントができました。
matcherはNimbleを使っています。Swift, Objective-Cの両方で使えます。Cedarの影響を受けているようです。
比較
テストメソッドの書き方
XCTestの場合
import XCTest
@testable import <プロジェクト名>
class <クラス名> : XCTestCase {
func testHoge() {
// テスト
}
}
基本形はこれです。場合によって以下のメソッドを使ったりします。
メソッド | 意味 |
---|---|
setUp(クラスメソッド) | クラスに存在する全てのテストが始まる前に実行される |
setUp(インスタンスメソッド) | 各テストが始まる前に実行される |
tearDown(インスタンスメソッド) | 各テストが終わった後に実行される |
tearDown(クラスメソッド) | クラスに存在する全てのテストが終わった後に実行される |
どのようなテストなのかは基本的にメソッド名に詰め込むので、場合によっては長いメソッド名になります。
サンプルコードから抜粋するとこんなかんじです。
func testDBManagerTest_findAllMethod_whenNotExistData_output0Data() {
// テスト
}
Quickの場合
import Quick
import Nimble
@testable import <プロジェクト名>
class <クラス名>: QuickSpec {
override func spec() {
describe("describe text") {
context("context text") {
it("it text") {
// テスト
}
}
}
}
}
基本形はこれです。ただしdescribe, context, itはなくても構いません。describe, contextに関しては入れ子にして複数使っても構いません。describeの引数にはクラスと関数を、contextの引数にはある条件下での動作を、itの引数には期待される結果を書きます。
公式によると、「厳密には context キーワードは describeと同じですがテストを理解しやすくなるので使い分けるとよいです。」とのことです。
また、場合によって以下のメソッドを使ったりします。
メソッド | 意味 |
---|---|
beforeSuite | クラスに存在する全てのテストが始まる前に実行される |
beforeEach | 各テストが始まる前に実行される |
afterEach | 各テストが終わった後に実行される |
afterSuite | クラスに存在する全てのテストが終わった後に実行される |
サンプルコードから抜粋するとこんなかんじです。
describe("DBManager test") {
describe("findAll method") {
context("when not exist data") {
it("output 0 data") {
// テスト
}
}
}
}
テストの書き方
とりあえず今回は実際の値と予想していた値が等しいことを確かめるテストを取り上げて比較してみようと思います。
XCTestの場合
XCTAssertEqual(results.count, 0, "Expect \(results.count) to equal 0")
第一引数に実際の値、第二引数に期待値、第三引数にエラーだった時に表示するメッセージを入れます。
Quickの場合
expect(results.count).to(equal(0))
expectの引数に実際の値、equalの引数に期待値を入れます。
エラーだった時に表示するメッセージを用意しなくてもデフォルトで出力されるテンプレートがあるので、特別書く必要はありません。
※エラーメッセージをカスタマイズすることもできます。
テストが失敗した時の表示
XCTestの場合
Quickの場合
比べてみてQuickいいなと思ったポイント
エラーメッセージをわざわざ書かなくていいのは楽
テストを大量に書くとなると少しでも手間が省けるのはだいぶうれしいです。
テストのまとまりがわかりやすい
サンプルコードではupdateメソッドのテストを2つのパターンに分けて書いています。
これをXCTestで書くと下記のようになるわけですが、
func testDBManagerTest_updateMethod_whenAddNewData_newDataIsInserted() {
// テスト
}
func testDBManagerTest_updateMethod_whenAddExistngData_theDataIsUpdated() {
// テスト
}
Quickで書くとこうなります。
describe("update method") {
context("when add new data") {
// テスト
}
context("when add existing data") {
// テスト
}
}
一つのテストクラスで一つのメソッドについてテストしている分には何も変わらないでしょうが、複数のメソッドを同じテストクラス内でテストしているときは階層がメソッドごとに分かれていたほうが見やすくて良いと思いました。