きり丸の技術日記

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

【2021】SpringBootでpropertiesやymlの設定ファイルが読み込めることのテストを書く

去年書いた記事で使用していたクラスがDeprecatedになっていたので、新しく更新します。

前回の記事を書いた時にアップデートしておけばよかったです。

変更点

  • ConfigFileApplicationContextInitializer から ConfigDataApplicationContextInitializer に変更
  • 設定ファイルの設定方法が変更(今回の記事には影響なし)

こちらの記事での記載の通り、SpringBoot2.4から設定ファイルの設定方法が変わりました。

ConfigFileApplicationContextInitializer では旧設定で動きますが、この機会に ConfigDataApplicationContextInitializer に変えつつ新設定に変更してみてください。

なお、今回の記事の内容では直接影響はありません。GitHubにはその対応を含めてアップロードしているので、興味があればソースコードも見てください。

ゴール

設定ファイルを読み込めていることを示すテストを書けるようにします。

環境

  • Java
    • 15
  • org.springframework.boot
    • 2.4.4

ソースコード

設定ファイル


下記のディレクトリに、設定ファイルを記載します。 src/test/resources/application.yml

app:
  config:
    appName: "Kirimaru"

設定ファイルを読み込むJavaファイル


ConfigurationPropertiesアノテーションを使用します。prefixをつけることで、特定の階層以降を読み込むことができるので、可読性が上がるのでオススメです。

下記ディレクトリにJavaファイルを格納する。 {ContextRoot}/config/CoreProperties.java

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Getter
@Setter
@ConfigurationProperties(prefix = "app.config")
public class CoreProperties {
  private String appName;
}

テストコード


SpringJunitConfigアノテーションと、EnableConfigurationPropertiesアノテーションを使用します。

設定ファイルから読み込ませて、目的通り読み込んでいることを確認する必要があるので、SpringJunitConfigのイニシャライザにConfigDataApplicationContextInitializer.classを指定する必要があります。

また、必要な設定ファイルを読み込ませるために、EnableConfigurationPropertiesに読み込ませたい設定ファイルを指定します。今回の例ではCoreProperties.classです。

{TestのContextRoot}/config/CorePropertiesTests.java

@SpringJUnitConfig(initializers = ConfigDataApplicationContextInitializer.class)
@EnableConfigurationProperties({CoreProperties.class})
class CorePropertiesTests {
  @Autowired
  CoreProperties coreProperties;

  @Test
  void test_01(){
    assertThat(coreProperties.getAppName()).isEqualTo("Kirimaru");
  } 
}

GitHubソースコード

  • 対象ファイル
    • application.yml(activeProfilesにutを指定)
    • application-external-ut.yml(profiles=utで動かしているので、設定値はこちら)
    • TwitterProperties.java
    • TwitterPropertiesTests.java

github.com

終わりに

既に前回の記事の時にはDeperecatedになっていましたが、アップデートしていないと気づけませんね。

旧設定は旧設定で大事ではありますが、バージョンをしっかり明記することで読者に正確な情報を提供できるようにしたいです。


この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。

参考記事

Baeldung www.baeldung.com

公式ヘルプ github.com

関連記事

SpringBootでpropertiesやymlの設定ファイルが読み込めることのテストを書く【Java】(前回の記事) nainaistar.hatenablog.com

Spring Profilesで環境ごとの設定ファイルを読み込む(SpringBoot2.4の変更も記載) nainaistar.hatenablog.com

f:id:nainaistar:20210328153643p:plain

2021/03の第4週に読んで参考になった記事の紹介

今週も読んで参考になった記事をまとめます。

気が向いたら、同じ内容を自分のブログとしても書きます。Zennのスクラップみたいな感じにしようと思っています。カテゴリも「スクラップ」にしました。

IntelliJ IDEAにて「Command line is too long」でテストが動かなくなった

GradleのタスクランナーをIntelliJ IDEAに変更したら、なぜか「Command line is too long」が出てテストができなくなったので、対応した時に参考にしました。

記事

Intellij IDEAのgradleプロジェクトビルド時に Command line is too long が出る場合の対処 qiita.com

同一クラス別パッケージのクラスをSpringでDIしたい

これは後でブログ記事にする予定です。

同一クラス別パッケージのクラスだと、Bean名が被ってしまいDIできないので何とかするための機能です。

いろふさんに教えてもらったFullyQualifiedAnnotationBeanNameGeneratorで解決はできました。

しかし、SpringBootTestよりも軽量で良く愛用しているWebMvcTestがいい感じにDIすることができませんでした。

同じようにComponentScanを使えばいいかと思っていましたが、WebMvcTestがのアノテーション内ではComponentScanではなくComponentScan.Filterしか使えないようなので対応策を悩み中です。

それを踏まえて、記事にする予定です。

記事

twitter.com

docs.spring.io

4%ルールは今でも生きているのか

技術記事ではないけれど、参考になった記事。

FIREで扱われる4%ルールに関して、現代でも同様の成果が得られるかどうかというのをシミュレーションをしている記事。

記事読んだ感想だけ言うと、FIREしたら年金は無いので3%で生活しないと厳しそうだと思いました。

目標金額を上方修正するか、目標がたまった時点での年金も考慮しないと厳しそうですね。

記事

資産運用の出口戦略「4%ルール」をシミュレーションしてみた! nukunukusas.com

終わりに

この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。

f:id:nainaistar:20210327132540p:plain

エンティティを渡すだけでDBのユニットテストのセットアップしたい・改

前にデータセットアップを面倒くさがって、BeanPropertySqlParameterSourceを使ってパラメータの項目名を取得していました。その時の記事。

そのまま使用していると、自分の目的通りには使えなかったので結局自作しました。

その時のメモ。

環境

  • Java
    • 15
  • H2
    • 1.4.200
  • org.springframework.boot:spring-boot-starter-web
    • 2.4.0
  • org.springframework.boot:spring-boot-starter-test
    • 2.4.0
  • org.mybatis.spring.boot:mybatis-spring-boot-starter-test
    • 2.1.3

制約

  • DBの項目はsnakeケース
  • Javaのエンティティの項目はcamelケース
  • エンティティは複合主キーだった場合、別のクラスが存在する
  • エンティティにID以外のエンティティは存在しない
  • エンティティにはsetter/getter以外のメソッドが存在しうる
    • 前回の記事にはなかった新しい制約
public class DummyIdDto implements Serializable {
  String idFirst;
  String idSecond;
}

public class DummyDto implements Serializable {
  DummyIdDto id;
  String fieldFirst;
  String fieldSecond;
  String fieldThird;

  // 新しい制約
  public boolean isDummy() {
    return id == null;
  }
}

上手くいかなかった原因

BeanPropertySqlParamerSourceのプロパティ名を取得するロジックを理解していなかったから。


今でも完全理解はしていませんが、setter/getterを元にプロパティ名を構築しているようです。

ですので、私が用意した「isDummy」メソッドはbooleanのgetterメソッドとみなされ、プロパティ名を構築してしまいます。

この「isDummy」から「dummy」プロパティ名を推測してしまいました。

-- 実際にはDummyカラムは無いので、SQLエラーになる
INSERT INTO table_name (dummy) values (?);

解決策

2通りあります。

  1. isXXX, getYYY等のsetter/getterの命名規則に沿ったメソッドは作らない
  2. BeanPropertySqlParameterSourceを加工する

今回の対応は後者となります。

setter/getterの両方が揃っているプロパティ名を、意図するプロパティ名とすることにしました。

具体的には、BeanPropertySqlParameterSourceの処理をコピペし、プロパティ名を取得するメソッドのgetReadablePropertyNamesのみを加工しました。

PropertyDescriptorクラスのgetWriteMethodメソッドにsetter, getReadMethodメソッドにgetterが設定されています。そのどちらも設定されていない場合を、自作メソッドとして処理し、プロパティ名としては扱わないようにしました。

  private boolean isMethod(PropertyDescriptor pd) {
    return pd.getWriteMethod() == null || pd.getReadMethod() == null;
  }

BeanPropertySqlParameterSourceを加工した処理全体。

public class CustomBeanPropertySqlParameterSource {
  private final BeanWrapper beanWrapper;

  @Nullable
  private String[] propertyNames;


  /**
   * Create a new BeanPropertySqlParameterSource for the given bean.
   *
   * @param object the bean instance to wrap
   */
  public CustomBeanPropertySqlParameterSource(Object object) {
    this.beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(object);
  }


  @Nullable
  public Object getValue(String paramName) throws IllegalArgumentException {
    try {
      return this.beanWrapper.getPropertyValue(paramName);
    } catch (NotReadablePropertyException ex) {
      throw new IllegalArgumentException(ex.getMessage());
    }
  }

  /**
   * Provide access to the property names of the wrapped bean.
   * Uses support provided in the {@link PropertyAccessor} interface.
   *
   * @return an array containing all the known property names
   */
  public String[] getReadablePropertyNames() {
    if (this.propertyNames == null) {
      List<String> names = new ArrayList<>();
      PropertyDescriptor[] props = this.beanWrapper.getPropertyDescriptors();
      for (PropertyDescriptor pd : props) {
        if (isMethod(pd)) {
          continue;
        }

        if (this.beanWrapper.isReadableProperty(pd.getName())) {
          names.add(pd.getName());
        }
      }
      this.propertyNames = StringUtils.toStringArray(names);
    }
    return this.propertyNames;
  }

  // 自作メソッド
  private boolean isMethod(PropertyDescriptor pd) {
    return pd.getWriteMethod() == null || pd.getReadMethod() == null;
  }

}

ソースコード

CustomBeanPropertySqlParameterSource.java github.com

テストコード:CommonSetupTests.java github.com

終わりに

よく分からない機能を、よくわからないまま使ったらだめですね。

まさか、メソッド名から逆引きしてプロパティ名を推測しているとは思いもしませんでした。

1つ、勉強になりました。


この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。

類似記事

エンティティを渡すだけでDBのユニットテストのセットアップしたい(SimpleJdbcInsertとBeanPropertySqlParameterSource) nainaistar.hatenablog.com

f:id:nainaistar:20210320182415p:plain