勉強会のために東京から福岡に遠征している @akatsuki174 です。滞在中にラーメンと明太子は必ず食べようと思ってます。
この記事は遠征中に参加している HAKATA Test Night #2 の登壇資料の文字版です。この勉強会には iOS エンジニアも Android エンジニアもいるので、両者が楽しめるよう広く浅く BDD テストフレームワークを紹介します。今回紹介する Android の Spek、iOS の Quick は似ている部分が多々あるので、比較しつつ書いてみます。
スライド資料はこちらです。
この記事に出てくるサンプルコードは GitHub に上げています( Quick に関しては以前「XCTestと比較しつつQuickについて説明する」という記事で使ったサンプルコードと同居させてます)。
Spek のサンプル: Spek2Sample
Quick のサンプル: QuickSample
ゴール
- Spek の基礎がわかる
 - Quick の基礎がわかる
 
概要
Spek とは
Kotlin製の、Kotlinのための BDD テストフレームワークです。2019年2月にメジャーバージョンが2になり、Breaking changes が入りました。大きいところでいうと DSL が Specification と Gherkin の2種類から好きな方を選んで書けるようになりました。Specification は RSpec ライクに書ける形式で、私はこちらの方が好きなのでこの記事では Specification 前提で話を進めます。
GitHub: https://github.com/spekframework/spek
ドキュメント:  http://spekframework.org/
※本記事執筆時点での最新バージョンは 2.0.9 。
Quick とは
Swift, Objective-C のための BDD テストフレームワークで、こちらも RSpec っぽっく書けます。日本語ドキュメントが充実してますし、導入事例もわりと多い印象なのでとっかかりやすいと思います。内部で使用されているマッチャーは Nimble です。Nimble は自然文に近い形で記述できるので理解しやすいです。
GitHub: https://github.com/Quick/Quick
※本記事執筆時点での最新バージョンは 2.2.0 。
導入方法
Spek
Spek は JUnit 5 が必要なのでそれも含めて準備します。ここでは Android フレームワークに依存する場合の gradle 記述例を記載します。
もし JUnit 4 で書く必要があるもの( Robolectric 等)がすでに存在する場合は junit-vintage が必須なので注意です。これによって前バージョンのテストも検知することができ、JUnit 5 上で JUnit 4 のテストを動かせます。
apply plugin: "de.mannodermaus.android-junit5" android { ... sourceSets.each { it.java.srcDirs += "src/$it.name/kotlin" } testOptions { junitPlatform { filters { engines { include 'spek2', 'junit-vintage' } } } unitTests.all { testLogging.events = ["passed", "skipped", "failed"] } } } dependencies { implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" // spek testImplementation "org.spekframework.spek2:spek-dsl-jvm:$spek_version" testImplementation "org.spekframework.spek2:spek-runner-junit5:$spek_version" testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" }
Quick
CocoaPods でも入れられますが、Carthage 前提で話を進めます。この2つのライブラリはテストでしか使わないので Cartfile.private に以下の記述をします。
github "Quick/Quick" github "Quick/Nimble"
carthage update からプロジェクトに import してビルドできるようにするまでの過程は他のライブラリ導入時と同じです。
テストコード
プロジェクト本体に FizzBuzz メソッドを作成し、それをテストするコードを書いてみます。
Spek
テストをグルーピングするときに使うキーワードをいくつか紹介します。
describe : テスト対象を記述することが多いようです。
context : テスト条件を記述することが多いようです。
it : 期待する結果を記述します。
import org.junit.Assert import org.spekframework.spek2.Spek import org.spekframework.spek2.style.specification.describe object FizzBuzzSpec : Spek({ val fizzBuzz = FizzBuzz() describe("FizzBuzz method") { // テスト対象を記述 context("when enter 2") { // テスト条件を記述 it("should return 2") { // 期待する内容を記述 Assert.assertEquals("2", fizzBuzz.fizzBuzz(2)) // JUnit 4 も使える } } context("when enter 3") { it("should return 'Fizz'") { Assert.assertEquals("Fizz", fizzBuzz.fizzBuzz(3)) } } context("when enter 5") { it("should return 'Buzz'") { Assert.assertEquals("Buzz", fizzBuzz.fizzBuzz(5)) } } context("when enter 15") { it("should return 'Fizz Buzz'") { Assert.assertEquals("Fizz Buzz", fizzBuzz.fizzBuzz(15)) } } } })
Quick
使うキーワード、意味は先程と同じです。
import Quick import Nimble @testable import QuickSample class FizzBuzzSpec : QuickSpec { override func spec() { let fizzBuzz = FizzBuzz() describe("fizzBuzz method") { // テスト対象を記述 context("when enter 2") { // テスト条件を記述 it("should return 2") { // 期待する内容を記述 expect("2").to(equal(fizzBuzz.fizzBuzz(num: 2))) // Nimble使用 } } context("when enter 3") { it("should return 'Fizz'") { expect("Fizz").to(equal(fizzBuzz.fizzBuzz(num: 3))) } } context("when enter 5") { it("should return 'Buzz'") { expect("Buzz").to(equal(fizzBuzz.fizzBuzz(num: 5))) } } context("when enter 15") { it("should return 'Fizz Buzz'") { expect("Fizz Buzz").to(equal(fizzBuzz.fizzBuzz(num: 15))) } } } } }
実行、実行結果
Spek
現在は Gradle を使用して特定のテストだけを実行する方法はありません。Android Studio で実行する場合は Spek Framework plugin を入れる必要があります。

これを入れることによってテストの横に三角矢印が付き、IDE から簡単にテスト実行ができるようになります。

試しにこれを実行してみるとこのように実行結果が表示されます。

正常に4つのテストが通過していることがわかります。ではここでわざとテストが失敗するように細工してみます。
it("should return 2") { Assert.assertEquals(2, fizzBuzz.fizzBuzz(2)) // ○ String, X Int }
すると、どこでどう間違っているのかがわかりやすく表示されます。

Quick
XCTest 同様、行数部分のマーククリックでテストを実行することができます。

テスト結果はこのように出てきます。

こちらもわざとテストを失敗させてみましょう。Spek とは異なり型違いの場合はそもそもビルドができないので、文字列を変えてみます。
it("should return 2") { expect("12").to(equal(fizzBuzz.fizzBuzz(num: 2))) }
間違いがわかりやすいですね。

まとめ
主観もりもりのまとめです。
- Spek / Quick は導入が簡単
 - 仕様書感あふれるテストが書ける
 - 他言語エンジニアでも読みやすい
 - ネストを活用して見やすく、わかりやすく書ける
 
ということでぜひ導入を検討してみてください!
最後に
子育て家族アプリFammを運営するTimers inc.では、現在エンジニアを積極採用中!
急成長中のサービスの技術の話を少しでも聞いてみたい方、スタートアップで働きたい方など、是非お気軽にご連絡ください!
採用HP:  http://timers-inc.com/engineerings