Cloud Spanner をエミュレーターを使いローカルテスト環境を構築してみた (Java)

今回は Cloud Spanner をローカル実行するエミュレーターを使い、そのエミュレーターに Java から接続し、ローカルテストを行う環境を構築してみました
2023.11.13

西田@CX事業部です

今回は Cloud Spanner をローカル実行するエミュレーターを使い、そのエミュレーターに Java から接続し、ローカルテストを行う環境を構築してみました

基本的に以下のページを元にこの記事は執筆しています

Cloud Spanner をローカルでエミュレートする

大枠の流れ

  1. Docker もしくは、 gcloud コマンドでローカル開発環境に Spanner のエミュレーターを起動
  2. gcloud にエミュレーターを使用するための設定を追加し、エミュレーターに instance を追加
  3. SPANNER_EMULATOR_HOST 環境変数をテスト実行してるプロセスに設定しローカル起動しているエミュレーターに接続

Spannerのエミュレーターを起動

Spanner のエミュレーターを起動する方法は以下の2通りの方法があります

  • gcloud コマンドで起動する
  • Docker で起動する

※ どちらの方法でも Docker は必要なので、あらかじめインストールしておく必要があります

今回はgloudコマンドで起動する方法でエミュレーターを起動します

gcloud emulators spanner start

上記コマンドで、 gRPC用がlocalhost:9010 で、REST用が localhost:9020 で起動します

エミュレーターをシャットダウンすると、データも削除されます

gcloud にエミュレーター用の Configuration を作成

gcloudコマンドからSpannerのエミュレーターに接続するには以下の設定をする必要があります

  • 認証の無効化
  • Spanner の endpoint を変更

これらの設定を毎回変えるのは大変なので、 gcloudのConiguration(構成) を作成しておきます

# configurationsを作成
gcloud config configurations create emulator # 任意の名前

# configurationsを切り替え 
gcloud config configurations active emulator

次にエミュレーターに接続するための設定をします

# 認証を無効化
gcloud config set auth/disable_credentials true

# gcloudコマンドのSpannerのEndpointをエミュレーターのREST用 Endpoint に変更
gcloud config set api_endpoint_overrides/spanner http://localhost:9020/

※ エミュレーターに接続する場合でも Project の設定は必要なのにご注意ください。

テスト用の Spanner Instance を作成

テストに使用する、Spanner の Instance を作成します インスタンスにつけた名称は、後ほどJavaから接続するときに使用します(以下の例の test-instance に当たる部分)

gcloud spanner instances create test-instance \
   --config=emulator-config --description="Test Instance" --nodes=1

Javaからエミュレーターに接続する

SPANNER_EMULATOR_HOST 環境変数を設定する

Java で利用する Spanner SDKが接続するEndpointを変更するために、 SPANNER_EMULATOR_HOST環境変数を設定します 直接 JUnit 等を実行するプロセスに対して環境変数を設定しても良いのですが、汎用的に起動できるようにしたかったため、 Javaからコードで環境変数を設定しました

Map<String, String> env = System.getenv();
Class<?> cl = env.getClass();
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Map<String, String> writableEnv = (Map<String, String>) field.get(env);
writableEnv.put("SPANNER_EMULATOR_HOST", "localhost:9010");

上記のコードを JUnit の @Before などに設定して使用します

Spannerのデータベースを作成する

glcoud コマンドで作成した Spanner のインスタンスにデータベースを作成します

まず、DatabaseAdminClient を生成し

SpannerOptions options = SpannerOptions.newBuilder().build();
    Spanner spanner = options.getService();
    DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient();

データベースを作成します。

データベース作成時に使用するテーブルを作成するDDLを一緒に実行できるので、合わせてテーブルも作成します

final String CREATE_TABLE_SQL = "CREATE TABLE Dog (id INT64 NOT NULL, name STRING(255)) PRIMARY KEY(id)";

OperationFuture<Database, CreateDatabaseMetadata> op = adminClient.createDatabase(
  "test-instance", // gcloud コマンドで指定したインスタンス名
  "test-db", 
  // データベース作成時に流すDDL
  Arrays.asList(
    CREATE_TABLE_SQL
  )
);

作成したデータベースにデータを投入します

以下は作成した Spanner にデータベースに Mutation を使ってデータを投入する例です

final List<Dog> dogs = 
  Arrays.asList(
    new Dog(1, "しば犬"),
    new Dog(2, "ブルドック")
  );

List<Mutation> mutations = new ArrayList<>();

for (Dog sample : dogs) {  
  mutations.add(
    Mutation.newInsertBuilder("Dog")
      .set("id").to(dog.id)
      .set("name").to(dog.name)
    .build()
  );
}

Spanner spanner = SpannerOptions.newBuilder().build().getService();
SpannerOptions options = SpannerOptions.newBuilder().build();
DatabaseId dbId = DatabaseId.of(options.getProjectId(), "test-instance", "test-db");
DatabaseClient dbClient = spanner.getDatabaseClient(dbId);

dbClient.write(mutations);

最後に

Spannerは公式にエミュレーターが用意されているので、簡単にローカルテスト環境を構築することができました。 自動テストを実行するのに、開発者の人数分 Spanenr のインスタンスを用意するのは現実的ではないので、こちらを利用して自動テストができると、開発が捗りそうです