きり丸の技術日記

技術検証したり、資格等をここに残していきます。

SpringのDisabledIfを素振りする

条件によってはテストを実行しないアノテーション、JUnit5DisabledIfだけでなく、SpringDisabledIfもあります。

基本的にはJUnit5DisabledIfシリーズで十分なことが多いのですが、Spring側で用意されているものも素振りします。

環境

  • Java
    • 17
  • SpringBootTest
    • 2.7.4

前提

設定ファイルの準備

SpringDisabledIfを使用する最大のメリットは、設定ファイルによって挙動を変更できるところです。そのため、application.ymlを定義します。

app:
  config:
    appName: "Kirimaru"
    local: "true"

テストクラスのセットアップ

テストで設定ファイルを読み込めるようにします。

// 設定ファイルを読み込むアノテーションとinitializers
@SpringJUnitConfig(initializers = ConfigDataApplicationContextInitializer.class)

他にも設定ファイルを読み込めるのであればSpringBootTest等々のアノテーションでも問題ありません。ただ、ユニットテストレベルでは不要なDI等々も行ってしまうので、SpringJUnitConfigのほうが早く起動できます。

@SpringBootTest

対応

Spring機能を使用したいので、loadContexttrueを設定します。

${path}

${path}を使用すると、設定ファイルの値を読み込めます。なお、読み込んだ値がtruefalseである必要があります。先ほどapp.config.localtrueを設定しているので、次のテストは動かないように制御できます。

@DisabledIf(value = "${app.config.local}", loadContext = true)
@Test
void test_01() {
  fail();
}

#{expression}

#{expression}を使用すると、中に条件式を記載できます。Springで設定ファイルを読み込むクラスはEnvironmentクラスです。Environmentクラスを利用して、app.config.appNameの値がkirimaruのときに、テストが動かないように制御するには次の式を使用します。

@DisabledIf(
  value = "#{environment.getProperty('app.config.appName').equalsIgnoreCase('kirimaru')}",
  loadContext = true
)
@Test
void test_02() {
  fail();
}

なお、#{}内部で${}は使用できないようです。できるのかもしれませんが、私には理解できませんでした。似たような書き方を聞いている掲示板はあるのですが、解決にはいたっていないようです。

ログを見る限り、app.config.appNameKirimaruは読み込めているのですが、Kirimaruを元にBean名を探しているようでした。

@DisabledIf(value = "#{${app.config.appName}.equalsIgnoreCase('kirimaru')}", loadContext = true)
org.junit.jupiter.engine.execution.ConditionEvaluationException: Failed to evaluate condition [org.springframework.test.context.junit.jupiter.DisabledIfCondition]: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'Kirimaru' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?

備考

JUnit5DisabledIfシリーズも調べるとおもしろいので、ぜひ興味があるときに調べてみてください。

org.junit.jupiter.apiの5.8.2には次のシリーズがありました。

  • DisabledIf
  • DisabledIfEnvironmentVariable
  • DisabledIfSystemProperty
  • DisabledOnJre
  • DisabledOnOs
  • DisabledForJreRange
  • および、上記とは逆のEnabledIfシリーズ

適切ではないですがローカルで動いてCI上で落ちるテストがある場合は、@DisabledOnOs(OS.LINUX)のように回避することもあります。

ソースコード

終わりに

SpringDisabledIfが便利に使えそうだったのですが、検証に時間がかかってしまいました。loadContexttrueにする必要があったり、SpEL式がうまく書けなかったり。

これを覚えたことで今後回避したいアノテーションを作るときには、ちょっとした作り込みをする必要がなくなったので、未来の工数を大きく削減できました。

特定の環境では動かしたくないテストは大量にあるので、今後も活用していきたいです。

参考情報

類似記事

vmwpを終了したい(kill vmwp)

ピアソンVUEの自宅受験(OnVUE (オンビュー) - オンライン監督)をしたかったのですが、vmwpのプロセスの殺し方が分からなかったので残します。

環境

  • Windows

対応

vmwpWSLで使用されているようです。そのため、WSLを停止したらvmwpは殺しきれるはずです。

wsl --shutdown

もし、vmwpのプロセスが残っている場合は、Dockerで使用している可能性があるので、Dockerも停止してください。

終わりに

OnVUEでの禁止アプリケーションとしてはExcel, Slack等々の名前が理解できるプロセス名だったのですが、vmwpだけはピンと来なくて困りました。検索キーワードが思いつけば解決できたと思うのですが、解決までに時間がかかってしまったので、超単純な記事ですが残しておくことにします。

Flywayで特定のDBだけSQLを実行する(Spring, vender, Postgres)

小ネタ。なお、私のユースケースを満たせていなかったので、調査結果だけです。

環境

  • Java
    • 17
  • org.flywaydb
    • 8.5.13
  • org.springframework.boot
    • 2.7.7

対応

設定ファイルの変更

PostgresやOracle等々で実行するSQLを変更するために、spring.flyway.locationsclasspath:db/migration/{vendor}を設定します。

spring.flyway.locations: classpath:db/migration/{vendor}

ディレクトリを作成する

ディレクトリを用意します。{vender}で置換できるのは、こちらのJavaファイルを確認してください。

今回は、h2, postgresql, mysqlを用意します。

- db
  - migration
    - h2
      - マイグレーションSQL
    - postgresql
      - マイグレーションSQL
    - mysql
      - マイグレーションSQL

後は、起動時、テスト時にDatabaseDriver名を元に特定のディレクトリのSQLが実行されていることが分かります。どのような動作確認をしていたかは、ソースコードで確認してください。

ソースコード

解決したかったユースケース

テスト時はH2を使用し、アプリケーション起動時はPostgresを使用しています。

ローカル環境構築時には、動作確認できるようにFlywayでマスタデータだけでなく、トランザクションデータを登録しようとしていました。

ただ、設定ファイルを書き換えたりする手間を考えると、逆にわかりづらいです。理想としては、{vender}の上位ディレクトリは汎用的なSQLを流しつつ、{vender}ディレクトリにトランザクションデータを登録させようとしていました。

- db
  - migration
    - 汎用SQL
    - postgresql
      - トランザクションSQL

このように配備したところ、子孫ディレクトリのSQLまで実行するため、汎用SQLもトランザクションSQLも実行されてしまいました。さらにclasspath:db/migration/{vendor}と設定すると、上位ディレクトリの汎用SQLが実行されなくなりました。

使えるユースケースもあると思いますが、かなり限定的だと思うので、私は使いません。以前、私が書いた記事の設定で十分です。

終わりに

この機能自体はFlywayの公式には載っていません。Spring側のDatabaseDriverで判別すると記載されているとおり、Springのラッパーライブラリ側で解決しています。

Flywayの公式ドキュメントを読んで存在せず、Spring側の公式ドキュメントを読んでようやく存在を認識しました。教えていただいたMitz Shiibaさん感謝です。

参考情報

類似情報