LoginSignup
3
1

More than 5 years have passed since last update.

Specs2のDSLテストケース内部で値クラスを使用する時のエラーについて

Posted at

Specs2のDSLでテストケースを書いていて、値クラス周りで少しつまづいたので、その時に発生したエラーとエラーの原因、その解決策を紹介します。

この記事中ではSpecs2と値クラスについての詳細な解説は行わないので、詳細な解説はドキュメントなどを読んでください。

環境

  • Scala 2.12.7
  • specs2-core 4.3.4

本文

エラーが発生する状況

Specs2は、以下のようなshouldinのようなDSLを使ってテストケースを記述することができます。

"#context" should {
    "testCase1" in {
        ...
    }
    "testCase2" in {
        ...
    }
}

そして、値クラスは、以下のようにAnyValを継承することで作成することができます。

case class ValueClass(value: String) extends AnyVal

ここで、Specs2のテストケース内部で、入力値として値クラスを何度も繰り返し使いたいということがありました。
テストケース毎に入力値は変化するので、以下のように値クラスの値をval変数として保持します。

"#context" should {
    "testCase1" in {
        val input = ValueClass("foo")
        ...
    }
    "testCase2" in {
        val input = ValueClass("bar")
        ...
    }
}

しかし、この状態でコンパイルを行うと、以下のようなエラーが発生してしまいます。

[error] Result type in structural refinement may not refer to a user-defined value class

「構造的部分型はユーザー定義の値クラスを参照しないかもしれない」といった意味のエラーだと思われます。では、なぜこのようなエラーが発生するのでしょうか?

原因

Specs2のDSLにおけるinで記述するテストケース内部の「値クラスのval変数への代入」が、「テストケースを表現する構造的部分型の公開メンバ(すなわち公開メソッド)」になっているので、「構造的部分型はメソッドのパラメータや戻り型に値クラスを取ることができない」というルールに違反していたことが原因として挙げられます。

ポイント

  • Specs2のDSLにおけるinで記述するテストケースの内部は、構造的部分型になっている
  • 構造的部分型はメソッドのパラメータや戻り型に値クラスを取ることができない

値クラスが構造的部分型のメソッドのパラメータや戻り型として扱うことができないことについては公式ドキュメントに記述があります。

回避策

原因はinで記述するテストケース内部で値クラスを公開メソッドの返り値にしていることです。
であれば、これを非公開にしてあげれば、これはメソッドではなくなりただの非公開メンバになります。
つまり「構造的部分型はメソッドのパラメータや戻り型に値クラスを取ることができない」のルールを回避できるわけですね。

最終的には以下のようなコードになります。

"#context" should {
    "testCase1" in {
        private val input = ValueClass("foo")
        ...
    }
    "testCase2" in {
        private val input = ValueClass("bar")
        ...
    }
}
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1