プログラマブル深海魚

目立たないけど華やかに

ScalaTestのウォークスルー

ScalaTestでどのようにテストを記述するのかを学習したため、メモ的に記事に内容を残します。

目次

テストの実行(sbt)

testタスク

プロジェクトに含まれるすべてのテストを実施するには、sbtのtestタスクを実行します。

testOnlyタスク

特定のテストクラスのみテストを実施するには、testOnlyタスクを実行します。
"testOnly {クラス名}"の形式で実行すると、指定されたクラスのテストのみが実行されます。また、クラス名にはワイルドカードも使用できます。

テストの記法

ScalaTestでは、いくつかの記法でテストを記述することができます。
記法はテストの宣言の見た目にのみ影響を与え、テストの内容には影響を与えません。そのため、その場で最適な記法を選ぶことができます。

自分で記法を選択するのが面倒な場合、ScalaTestの公式DocではFlatSpecが推奨されています。

以下では一部の記法を紹介し、それ以降ではWordSpecスタイルを採用します。

FunSuiteスタイル

FunSuiteスタイルは非常にシンプルな記法で、xUnit(例えばJUnit)に近い記法です。

import org.scalatest.funsuite.AnyFunSuite

class FunSuiteTest extends AnyFunSuite {
  test("A negative number should be less than 0") {
    assert(-1 < 0)
  }
}

FlatSpecスタイル

FlatSpecスタイルはFunSuiteに近い記法ですが、"X should Y"や"X must Y"といった形式で記述します。
FunSuiteスタイルを、やや自然言語のように記述できるようにしたといった感じでしょうか。

import org.scalatest.flatspec.AnyFlatSpec

class FlatSpecTest extends AnyFlatSpec {
  "A negative number" should "be less than 0" in {
    assert(-1 < 0)
  }
}

FunSpecスタイル

FunSuiteスタイルを入れ子構造にできるようにしたような記法です。
RubyRSpecと似た記法のようで、describeとitによりテストを入れ子で記述します。

import org.scalatest.funspec.AnyFunSpec

class FunSpecTest extends AnyFunSpec {
  describe("A number") {
    describe("when negative") {
      it("should be less than 0") {
        assert(-1 < 0)
      }
    }
  }
}

WordSpecスタイル

こちらはFlatSpecスタイルを入れ子構造にしたような記法です。
Scalaの別のテストフレームワークであるspecs/specs2と似た記法です。

import org.scalatest.wordspec.AnyWordSpec

class WordSpecTest extends AnyWordSpec {
  "A number" when {
    "negative" should {
      "be less than 0" in {
        assert(-1 < 0)
      }
    }
  }
}

アサーション(Assertions)

アサーションを使用してテストを記述することができます。
以下で、一部のアサーションを紹介します。

assert

assertは記述した条件がtrueの場合に成功し、falseの場合に失敗します。

"A Set" when {
  "empty" should {
    "have size 0" in {
      assert(Set.empty.size == 0)
    }
  }
}

assertResult

あるコードの結果が特定の値になることを確認したい場合、assertResultを利用できます。

"A Set" when {
  "singleton" should {
    "have size 1" in {
      assertResult(1) {
        Set("test").size
      }
    }
  }
}

assertThrows

あるコードが特定の例外をスローすることを確認したい場合、assertThrowsを利用できます。

"A Set" when {
  "empty" should {
    "produce NoSuchElementException when head is invoked" in {
      assertThrows[NoSuchElementException] {
        Set.empty.head
      }
    }
  }
}

intercept

assertThrowsと同様に例外をスローすることを確認できますが、テストを中断せず例外の内容をさらにテストできます。

"A Set" when {
  "empty" should {
    "produce NoSuchElementException including 'empty' in message when head is invoked" in {
      val caught = intercept[NoSuchElementException] {
        Set.empty.head
      }
      assert(caught.getMessage.contains("empty"))
    }
  }
}

succeed/fail

テストを強制的に成功/失敗させます。

"A test" should {
  "succeed" in {
    succeed
  }

  "fail" in {
    fail("Always fails.")
  }
}

マッチャー(Matchers)

ScalaTestには、アサーションを"should"や"must"といった単語を使用して記述するためのDSLドメイン固有言語)が定義されています。 これをマッチャー(Matchers)と言います。
これらは、should.Matchersまたはmust.Matchersをミックスインすることで使用できます。

以下では、一部のマッチャーを紹介します(Shouldマッチャーを使用します)

等価性

equal, not equalを用いると、等価性をチェックできます。
また、be, not beも用いることができますが、こちらは等価性のカスタマイズができません(デフォルトの等価性チェック)

"A Set" when {
  "empty" should {
    "have size 0" in {
      Set.empty.size should equal (0)
    }
    "have not size 1" in {
      Set.empty.size should not equal (1)
    }
  }
}

より大きい・小さい

<, > , <=, >= を利用すると、より大きい・小さいや以上・以下の比較ができます。

"A number" when {
  "negative number" should {
    "less than 0" in {
      -1 should be < 0
    }
  }
  "positive number" should {
    "greater than 0" in {
      1 should be > 0
    }
  }
  "0" should {
    "0 or less" in {
      0 should be <= 0
    }
  }
}

ある数から一定の範囲内

ある数nから一定の範囲dの中に数値が入っているかを、"+-"を利用してチェックできます。小数も可能。

"A number" when {
  "5" should {
    "in range 6 +- 2" in {
      5 should equal (6 +- 2)
    }
  }
}

サイズ・長さ

have size, have lengthを用いると、サイズや長さのチェックができます。

"A Set" when {
  "empty" should {
    "have size 0" in {
      Set.empty[Number] should have size 0
    }
  }
}
"'ABC'" should {
  "have length 3" in {
    "ABC" should have length 3
  }
}

Booleanプロパティ

shouldBeのあとにプロパティ名のシンボルを記述すると、Booleanプロパティの真偽値をチェックできます。

"A Set" when {
  "empty" should {
    "be empty" in {
      Set.empty[Number] shouldBe Symbol("isEmpty")
    }
  }
}

参考文献

ScalaTest User Guide
https://www.scalatest.org/user_guide

ScalaTest入門 - Qiita
https://qiita.com/verdoyant/items/e8d81e80268b714fdfbc

Scalaユニットテスト入門 - seratch's weblog in Japanese
https://seratch.hatenablog.jp/entry/20110807/1312726957

【STEP4】テスト : Scalaでコードを書く - Qiita
https://qiita.com/yukinagae/items/038f75e9a1bf17978886

Scalatest: 特定のテストケースだけ実行したい - Qiita
https://qiita.com/suin/items/0294a53d6babd69f29a9