きり丸の技術日記

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

JavaにてJSONからインスタンスを生成する(テスト時に重宝)

個人的にはテストで多用していることが多いです。JacksonライブラリのObjectMapper最高ですね。

具体的にどのようなユースケースで使用しているかという点に関しては、一番最後に記載しています。

なお、Jacksonライブラリを使用したObjectMapperの記事は大量に検索できます。ですので、きり丸の使用するユースケースの説明が独自ポイントとなります。

環境

  • Java
    • 17
  • org.springframework.boot
    • 2.6.4
  • (com.fasterxml.jackson.core)
    • 2.13.1
  • (commons-io)
    • 2.6

ユースケース

  • テスト時にファイルからインスタンスを生成する
    • 巨大なインスタンスだと分かりづらいので、JSONで構造を把握したい

コード

ObjectMapper#readValueを使用します。第1引数にJSONファイルに対するクラス、第2引数にマッピング先の型を設定します。

第1引数にはURL, InputStream, Fileクラス等々を渡せるように複数のメソッドが用意されています。第2引数にはClass<T>, TypeReference<T>, JavaTypeクラス等々を渡せます。

シンプルに使用

プロダクトコード

個人的には、テストクラス側での使いやすさを考慮したInputStreamを使用するメソッドをラップして用意しています。そのまま使用すると検査例外(IOException)が発生するので、ラップして実行時例外(RuntimeException)にするとテストで扱いやすくなります。

// JSONファイルからclazzにマッピングする
  <T> T fromJson(InputStream input, Class<T> clazz) {
    try {
      return objectMapper.readValue(input, clazz);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

テストコード

テストクラス側は次のように使用します。Class#getResourceAsStreamを使用するとresourcesディレクトリ配下から指定すればよいので、便利です。

  @Test
  public void test_02() {
    var input = getClass().getResourceAsStream("/kirimaru/human.json");

    Human expect = Human.builder()
        .age(18)
        .name("kirimaru")
        .build();

    Human actual = helper.fromJson(input, Human.class);

    assertThat(actual).isEqualTo(expect);
  }

JSONファイルの文字コードを考慮

プロダクトコード

JSONファイルの文字コードを考慮する場合、いったん文字列にする必要があります。commons-ioIOUtils.toStringを使用して、文字コードを考慮してファイルを読み込みます。

ファイルを扱う際はPathクラスだと、とても簡単になるので私はPathクラスでラップしています。

  <T> T fromJson(Path path, Class<T> clazz) {
    File file = path.toFile();
    try (FileInputStream input = new FileInputStream(file)) {
      return objectMapper.readValue(IOUtils.toString(input, StandardCharsets.UTF_8), clazz);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  } 

テストコード

特に解説しません。

  @Test
  public void test_03() {
    Path path = Paths.get("src", "test", "resources", "kirimaru", "human.json");

    Human expect = Human.builder()
        .age(18)
        .name("kirimaru")
        .build();

    Human actual = helper.fromJson(path, Human.class);

    assertThat(actual).isEqualTo(expect);
  }

ソースコード

終わりに

今回の代表的なユースケースとしては、複雑なドメインになりがちなユーザーのインスタンス作成です。携帯を購入する、というユースケースをユーザーのロールごとに考えると分かりやすくなるでしょう。

ロール 説明
利用者 実際にサービスを利用しているユーザー。子ども等々
契約者 サービスを契約したユーザー。契約したパパ等々
支払者 サービスに対して金銭を支払うユーザー。ママが支払う等

利用者が未成年の場合、保護者が必要です。利用者が外国籍の場合は日本籍より厳密な身分証明書を求められることもあるでしょう。支払者はクレジットカード、銀行引き落とし等々、お金を払う手段を用意していなければなりません。また、すべてのロールを同一人物が行うパターンもあります。

ユーザーのロールによっては必須なデータ、非必須なデータがあります。一からJavaで生成していると記載する項目が多く、設定した条件が分かりづらくなってしまうので、JSONとして確認すると分かりやすくできます。

なお、Rubyではテストセットアップに使用するFactoryBotが優秀ですので、JSONファイルにしない方がよいでしょう。Javaだとスマートにテストセットアップできなかったので、JSONファイルで扱っていました。


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