Timers Tech Blog

グローバルな家族アプリFammを運営するTimers inc (タイマーズ) の公式Tech Blogです。弊社のエンジニアリングを支える記事を随時公開。エンジニア絶賛採用中!→ https://timers-inc.com/engineering

#Spek / #Quick で BDD 環境を整えよう #hakata_test_night

勉強会のために東京から福岡に遠征している @akatsuki174 です。滞在中にラーメンと明太子は必ず食べようと思ってます。

この記事は遠征中に参加している HAKATA Test Night #2 の登壇資料の文字版です。この勉強会には iOS エンジニアも Android エンジニアもいるので、両者が楽しめるよう広く浅く BDD テストフレームワークを紹介します。今回紹介する Android の Spek、iOS の Quick は似ている部分が多々あるので、比較しつつ書いてみます。

testnight.connpass.com

スライド資料はこちらです。

この記事に出てくるサンプルコードは 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 を入れる必要があります。

f:id:akatsuki174:20200119220547p:plain

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

f:id:akatsuki174:20200120225305p:plain

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

f:id:akatsuki174:20200120230735p:plain

正常に4つのテストが通過していることがわかります。ではここでわざとテストが失敗するように細工してみます。

it("should return 2") {
    Assert.assertEquals(2, fizzBuzz.fizzBuzz(2))  // ○ String, X Int
}

すると、どこでどう間違っているのかがわかりやすく表示されます。

f:id:akatsuki174:20200122114944p:plain

Quick

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

f:id:akatsuki174:20200122112222p:plain

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

f:id:akatsuki174:20200122114722p:plain

こちらもわざとテストを失敗させてみましょう。Spek とは異なり型違いの場合はそもそもビルドができないので、文字列を変えてみます。

it("should return 2") {
    expect("12").to(equal(fizzBuzz.fizzBuzz(num: 2)))
}

間違いがわかりやすいですね。

f:id:akatsuki174:20200122114527p:plain

まとめ

主観もりもりのまとめです。

  • Spek / Quick は導入が簡単
  • 仕様書感あふれるテストが書ける
  • 他言語エンジニアでも読みやすい
  • ネストを活用して見やすく、わかりやすく書ける

ということでぜひ導入を検討してみてください!

最後に

子育て家族アプリFammを運営するTimers inc.では、現在エンジニアを積極採用中!
急成長中のサービスの技術の話を少しでも聞いてみたい方、スタートアップで働きたい方など、是非お気軽にご連絡ください!
採用HP: http://timers-inc.com/engineerings

Timersでは各職種を積極採用中!

急成長スタートアップで、最高のものづくりをしよう。

募集の詳細をみる