entry-header-eye-catch.html
entry-title-container.html

entry-header-author-info.html
Article by

Scala でテストを分類して外部の API を叩く

はじめましての方ははじめまして。ピクシブで Scala エンジニアとして働いている Javakky です。この記事では、当プロジェクトを担当してくれた piyo さんに代わってお送りさせていただきます。


今回は、「外部の API を実際に叩いて確認するテストも欲しいよね」というモチベーションからテストを分類して実行分けを行った話についてレポートしていきます。

検討した手法

前提として、弊チームでは Scala + sbt + Play Framework という構成で開発を行なっており、テストには ScalaTest を用いています。

チーム内で話し合った結果、以下の2手法がよさそうという話になりました。

(1) ScalaTest: Tagging your tests

ScalaTest には Tagging your tests という機能があり、各テストにタグというものを付与して実行時にフィルタリングをすることができます。 www.scalatest.org tagクラスを継承したオブジェクトを作成し、 test 設定時に渡すことで、コマンドからフィルタリング (のみ、除外) を行うことができます。

object SlowTest extends Tag("SlowTest")
import org.scalatest.FunSuite

class MySuite extends FunSuite {

  test("my test", SlowTest) {
    Thread.sleep(1000)
  }
}

また、 ~test-only * -- -n SlowTest などのようにオプションを利用することで、特定のタグがついたテストのみを実行することができます。 www.scalatest.org www.scalatest.org

(2) sbt: Custom test configuration

sbt 側の設定でも、テストを複数に分類することができます。 build.sbt Test を継承した定義を作成し、 src/ に専用のディレクトリを作成することでテストを分割することができます。

lazy val FunTest = config("fun") extend(Test)
......
FunTest / scalaSource := baseDirectory.value / "src/test-ext/scala"
......
lazy val myproject = project
   .configs(FunTest)
   .settings( /* 略 */ )

実行する場合はこのようになります。

FunTest/testQuick

ちなみに、ここでプロジェクト名のように指定できる値は、 Test を継承した変数名か、 config に渡した文字列の先頭を大文字にしたものになります。 www.scala-sbt.org

結果

(2) の sbt: Custom test configuration の手法を選択しました。
理由としては、
・タグでフィルタリングよりも専用のディレクトリを作成した方が、通常のテストに紛れてしまうことを防げそう
・テストに対する関心として別物なので、別のディレクトリで管理するのが妥当に感じる
などの意見があったためです。

まとめ

外部の API を叩くテストの環境を用意し定期的に GitLab CI で実行することで、実際には動作していない API リクエストなどが紛れ込んだ場合や、利用しているサービスが仕様変更を行った際に速やかに気づける仕組みが整備できました。 この手法はテストを分類したいというモチベーションであれば適用できると思いますので、ぜひご活用ください

追記

FunTest のために作成したフォルダ test-ext に配置された Scala ファイルはこのままでは ScalafixScalafmt といった外部ツールの影響からは外れてしまいます。2つのツールの個別設定を見ていきましょう。

Scalafix は公式ドキュメントの Integration tests に記載があります。

lazy val myproject = project
   .configs(FunTest)
   .settings(
     // 略
     scalafixConfigSettings(FunTest)
   )

Scalafmt も公式ドキュメントの Enable IntegrationTest の通りに実装しましょう。

lazy val myproject = project
   .configs(FunTest)
   .settings(
     // 略
     inConfig(FunTest)(ScalafmtPlugin.scalafmtConfigSettings)
   )
icon
javakky
決済周りの改善を中心に働いている2021年入社エンジニア。その名の通りJavaが好きなことで有名(?)で、最近はScalaを使える部署へ入ったらしい。