リモート開発メインのソフトウェア開発企業のエンジニアブログです

Scala のテストライブラリのバージョン互換の件で苦労した話

とある Spark のプロジェクトのテストに新たに spark-testing-base を追加しようとしたらバージョン互換の件でうまく動かず、一旦解決したと思ったら今度は既存のテストが動作しなくなったりと苦労したので解決策をメモしておきます。

結論から書いておくと、今回 2 つの問題が重なっていて、 1 つは spark-testing-base と ScalaTest の互換性、そして 2 つめは ScalaTest と Scalactic のバージョンの不一致が原因でした。

今回、問題が起きた原因を経緯を交えて紹介したいと思います。

プロジェクト概要ざっくり

  • Spark 2.4.3 on Scala 2.11
    • AWS Glue 2.0 の為ランタイムが決められており、しかも古い
  • 既存のテストコードは ScalaTest 3.1.x 系で書かれていた
    • ※この時は特に留意していなかったが Scalactic も 3.1.x 系の同じバージョンの物が使用されていた

やりたかった事

Spark のプロジェクトなので、 DataFrame の検証には spark-testing-base を使いたくなりました。
(spark-testing-base については以前弊社のメンバーがブログを書いています)

spark-testing-base は Spark のバージョン_spark-testing-base のバージョン と言う形式でバージョニングしているので、今回は Spark 2.4.3 (かつ Scala 2.11) に対応する最新版の 2.4.3_0.14.0 をインストールしました。

この状態でテストを実行してみると、コンパイルは通るものの、以下の assertDataFrameEquals の部分でランタイムエラーが発生してしまいました:

test("foo") {
  // ...
  
  assertDataFrameEquals(dataFrameA, dataFrameB)
}
An exception or error caused a run to abort: org.scalatest.Assertions.assertionsHelper()Lorg/scalatest/Assertions$AssertionsHelper;
java.lang.NoSuchMethodError: org.scalatest.Assertions.assertionsHelper()Lorg/scalatest/Assertions$AssertionsHelper;
Moba Pro

まずググってみて分かった事

雑にエラー名でググってみると、それっぽい公式リポジトリの Issue がすぐ見つかります。

NoSuchMethodError: org.scalatest.Assertions.assertionsHelper #315

本記事執筆時点 (2021/11/16) でまだ Open です。これによると、 (使っている spark-testing-base のバージョンが記載されていませんが) ScalaTest 3.1.x だと NoSuchMethodError が発生する様でした。
コメントにもある通り、 3.0.x (この時点で最新は 3.0.9) にダウングレードすると動作しそうな雰囲気でした。

実際に ScalaTest を 3.0.9 にダウングレードしてみると

以下の通り build.sbt を変更して ScalaTest のバージョンをダウングレードしてみました:

"org.scalatest" %% "scalatest" % "3.0.9" % Test,

すると今度は、今回追加したテストとは別の既存のテストがコンパイル時点でエラーになる様になってしまいました:

exception during macro expansion: 
java.lang.NoSuchMethodError: org.scalactic.BooleanMacro.<init>(Lscala/reflect/macros/whitebox/Context;Ljava/lang/String;)V
	at org.scalatest.AssertionsMacro$.assert(AssertionsMacro.scala:34)

    assert(count0 === 0)

状況を切り分ける為、一旦このテストをビルド対象から外して今回のテストを再実行したところ、正常に assertDataFrameEquals が動作する様になったので、 ScalaTest のバージョンはこのままで行きたいと思いました。

次に、エラーの内容を詳細に見てみると、 NoSuchMethodError を出しているのが Scalactic に書かれているマクロだと言う事が分かります。

Scalactic のバージョンもダウングレードする事に

その後色々調べてみて分かったのですが、冒頭に説明した通り、 Scalactic はインストール済みの ScalaTest と同じバージョンの物をインストールする必要があるので、これも一緒にダウングレードする必要がありました。

次の様に再度 build.sbt を変更します:

"org.scalactic" %% "scalactic" % "3.0.9" % Test,

これで無事に既存のテスト、そして今回追加したテストも全て動作する様になりました。

因みにこの事は公式ドキュメントにも記載されているのですが、この 2 つのパッケージはそれぞれ同じバージョニングとなっていて、双方を一致させる必要があります:

For every release of ScalaTest, there is a corresponding release of Scalactic with the same version number. Thus if you use both Scalactic and ScalaTest in the same project, you should make sure the version numbers match.

https://www.scalactic.org/older_releases より

所感

今回、問題が起きた理由としては次が考えられる:

  • spark-testing-base 2.4.3_0.14.0 は ScalaTest 3.0.x までしか動作しないが、依存関係の定義上は動作しない 3.1.x や 3.2.x も許可している
  • コンパイルエラーではなくてランタイムエラーなのでより原因の切り分けがしづらい
  • ScalaTest についても Scalactic のバージョン指定を固定していないので、 build.sbt 上では異なるバージョンをインストールする事が可能

例えば別プラットフォームで言うと NPM, Composer, RubyGems 等では依存するパッケージのバージョンを固定したり、マイナーバージョンアップまで許可とか細かく指定できるのだが、 build.sbt にはその様な機能は無いのだろうか?と感じた (あったら教えて下さい)

← 前の投稿

Pythonを使ったSlackの投稿方法およびProxyについて

次の投稿 →

Puma を systemd のユーザーサービスとして起動する

コメントを残す