Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

ScalaTestではshould ===を使うと良い

ScalaのテストフレームワークであるところのScalaTestのドキュメントを読んでいたら便利なメソッドを発見したのでメモ。実はもうみんな知ってるかもしれない。

等価性比較

ScalaTestでは、Matcherという便利なメソッドを使って等価性や存在判定などのテストを行うことができるようになっている。

import org.scalatest._
import flatspec._
import matchers._
import org.scalatest.funspec.AnyFunSpec

abstract class UnitSpec extends AnyFunSpec with should.Matchers {}

class FooSpec extends UnitSpec {
  describe("Int") {
    in("addition") {
      (1 + 1) shouldBe 2 // このshouldBeがMatcher
    }
  }
}

最も良く使われるであろうMatcherが、shouldBeマッチャーだ。

shouldBe マッチャー

shouldBeは、 中置演算子のように振る舞い、その等価性を判定する。等価性の判定は、以下の原則に従う:

  • ArrayであればSeqに変換して==で比較する
  • それ以外の型であれば==で比較する

shouldBeを使うメリットは、ほぼ型に左右されない(implicitを要求しない)ためコンパイルがもっとも高速であることだ。

should be(...)shouldBeと等価だが、shouldBeは括弧が不要なのでこちらを使ったほうが使い勝手が良いだろう。

shouldEqualマッチャー

次点でよく使われるのがshouldEqualマッチャーだ。

shouldEqualはほぼshouldBeと同じである。唯一違うのは、その振る舞いをorg.scalactic.Equality[A]を定義することでカスタマイズ可能である点だ。

Equalityを定義するメリットは色々ある。例えばプロダクトがDDDを採用していて、「IDが同じであればエンティティとして等価とみなす」という取り決めがなされている場合、オブジェクト全体を比較するのはナンセンスだし遅い。そこで、idだけ比較するようなEqualityを定義することで、等価性の判定を拡張できるのだ。

implicit val FooEq = new org.scalactic.Equality[Foo] {
  def areEqual(a: Foo, b: Any): Boolean = ???
}

このような定義を置いておくと、shouldEqualは自動的にFooEqを発見して等価性比較に使うようになる。

val foo1 = Foo(...)
val foo2 = Foo(...)

foo1 shouldEqual foo2 // FooEqが使われる

should be同様に、should equalという構文もあるが、shouldEqualと書いたほうが括弧を省略できるのでこちらが便利だ。

Productやcase classの場合

Equalityは直接比較した場合にのみうまく働くようで、タプルやコレクションに入れている場合は働かない。例えば、List[Foo]同士の比較にあたってはEquality[Foo]は使われない・・・。この点はちょっとなんとかなってほしい。

should ===マッチャー

あまり知られていない名マッチャーとしてshould ===がある。これはshouldEqualと同じだが、org.scalactic.TypeCheckedTripleEqualsと組み合わせて使うことで、左右の型が合っていることをコンパイル時に強制する。

class FooSpec extends UnitSpec {
  describe("Int") {
    in("tripleEqual") {
      42 should === ("foo") // 実行時にエラーになる
    }
  }
}
class FooSpec extends UnitSpec with org.scalactic.TypeCheckedTripleEquals {
  describe("Int") {
    in("tripleEqual") {
      42 should === ("foo") //コンパイルエラーになる
    }
  }
}

ほとんどの場合、テストでの比較対象は型が揃っているはず。テスト体験を良くするためにも、コンパイル時点で比較対象の型が合っていることを確認できる`should ===をおすすめしたい。

他にも、implicitな関係も含めて比較してくれるConversionCheckedTripleEqualsもあるので試してみてほしい。

参考文献

www.scalatest.org

https://www.scalactic.org/scaladoc/3.2.15/org/scalactic/Equality.htmlwww.scalactic.org

tullio.hatenablog.com

https://www.scalactic.org/user_guide/ConstrainedEqualitywww.scalactic.org

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?