Skip to content

GCloud Module

Testcontainers module for the Google Cloud Platform's Cloud SDK.

Install

npm install @testcontainers/gcloud --save-dev

The module supports multiple emulators. Use the following classes:

Emulator Class Container Image
Firestore (Native mode) FirestoreEmulatorContainer gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators
Firestore (Datastore mode) DatastoreEmulatorContainer gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators
Cloud PubSub PubSubEmulatorContainer gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators
Cloud Storage CloudStorageEmulatorContainer fsouza/fake-gcs-server:1.52.2
BigQuery BigQueryEmulatorContainer ghcr.io/goccy/bigquery-emulator:0.6.6
Cloud Spanner SpannerEmulatorContainer gcr.io/cloud-spanner-emulator/emulator:1.5.37

Examples

Firestore (Native mode)

it("should work using default version", async () => {
  await using firestoreEmulatorContainer = await new FirestoreEmulatorContainer(IMAGE).start();

  await checkFirestore(firestoreEmulatorContainer);
});
it("should work using version 468.0.0", async () => {
  await using firestoreEmulatorContainer = await new FirestoreEmulatorContainer(
    "gcr.io/google.com/cloudsdktool/google-cloud-cli:468.0.0-emulators"
  ).start();

  await checkFirestore(firestoreEmulatorContainer);
});

Firestore (Datastore mode)

it("should work using default version", async () => {
  await using datastoreEmulatorContainer = await new DatastoreEmulatorContainer(IMAGE).start();

  await checkDatastore(datastoreEmulatorContainer);
});
it("should work using version 468.0.0", async () => {
  await using datastoreEmulatorContainer = await new DatastoreEmulatorContainer(
    "gcr.io/google.com/cloudsdktool/google-cloud-cli:468.0.0-emulators"
  ).start();

  await checkDatastore(datastoreEmulatorContainer);
});

Cloud PubSub

import { PubSub } from "@google-cloud/pubsub";
import { getImage } from "../../../testcontainers/src/utils/test-helper";
import { PubSubEmulatorContainer, StartedPubSubEmulatorContainer } from "./pubsub-emulator-container";

const IMAGE = getImage(__dirname);

describe("PubSubEmulatorContainer", { timeout: 240_000 }, () => {
  it("should work using default version", async () => {
    await using pubsubEmulatorContainer = await new PubSubEmulatorContainer(IMAGE).start();

    await checkPubSub(pubsubEmulatorContainer);
  });

  async function checkPubSub(pubsubEmulatorContainer: StartedPubSubEmulatorContainer) {
    expect(pubsubEmulatorContainer).toBeDefined();

    const pubSubClient = new PubSub({
      projectId: "test-project",
      apiEndpoint: pubsubEmulatorContainer.getEmulatorEndpoint(),
    });
    expect(pubSubClient).toBeDefined();

    const [createdTopic] = await pubSubClient.createTopic("test-topic");
    expect(createdTopic).toBeDefined();
    // Note: topic name format is projects/<projectId>/topics/<topicName>
    expect(createdTopic.name).toContain("test-topic");
  }
});

Cloud Storage

The Cloud Storage container uses a fake Cloud Storage server by Francisco Souza.

it("should work using default version", async () => {
  await using cloudstorageEmulatorContainer = await new CloudStorageEmulatorContainer(IMAGE).start();

  await checkCloudStorage(cloudstorageEmulatorContainer);
});

BigQuery

The BigQuery emulator is by Masaaki Goshima and uses go-zetasqlite.

import { BigQuery, TableSchema } from "@google-cloud/bigquery";
import { getImage } from "../../../testcontainers/src/utils/test-helper";
import { BigQueryEmulatorContainer, StartedBigQueryEmulatorContainer } from "./bigquery-emulator-container";

const IMAGE = getImage(__dirname, 2);

describe("BigQueryEmulatorContainer", { timeout: 240_000 }, () => {
  it("should work using default version", async () => {
    await using bigQueryEmulatorContainer = await new BigQueryEmulatorContainer(IMAGE).start();

    await checkBigQuery(bigQueryEmulatorContainer);
  });

  async function checkBigQuery(bigQueryEmulatorContainer: StartedBigQueryEmulatorContainer) {
    expect(bigQueryEmulatorContainer).toBeDefined();
    const testDataset = "test-dataset";
    const testTable = "test-table";
    const testSchema: TableSchema = { fields: [{ name: "message", type: "STRING" }] };
    const config = {
      projectId: bigQueryEmulatorContainer.getProjectId(),
      apiEndpoint: bigQueryEmulatorContainer.getEmulatorEndpoint(),
    };
    const bigQuery = new BigQuery(config);

    await bigQuery.dataset(testDataset).create();
    await bigQuery.dataset(testDataset).table(testTable).create({ schema: testSchema });
    await bigQuery
      .dataset(testDataset)
      .table(testTable)
      .insert([{ message: "Hello, BigQuery!" }]);

    const [rows] = await bigQuery.dataset(testDataset).table(testTable).getRows();

    expect(rows).toEqual([{ message: "Hello, BigQuery!" }]);
  }
});

Cloud Spanner

The Cloud Spanner emulator container wraps Google's official emulator image.

it("should start, expose endpoints and accept real client connections using explicitly configured client", async () => {
  await using container = await new SpannerEmulatorContainer(IMAGE).withProjectId("test-project").start();

  const client = new Spanner({
    projectId: container.getProjectId(),
    apiEndpoint: container.getHost(),
    port: container.getGrpcPort(),
    sslCreds: container.getSslCredentials(),
  });

  // list instance configs
  const admin = client.getInstanceAdminClient();
  const [configs] = await admin.listInstanceConfigs({
    parent: admin.projectPath(container.getProjectId()),
  });

  // emulator always includes "emulator-config"
  const expectedConfigName = admin.instanceConfigPath(container.getProjectId(), "emulator-config");
  expect(configs.map((c) => c.name)).toContain(expectedConfigName);
});
it("should start, expose endpoints and accept real client connections using projectId and SPANNER_EMULATOR_HOST", async () => {
  await using container = await new SpannerEmulatorContainer(IMAGE).withProjectId("test-project").start();

  // configure the client to talk to our emulator
  process.env.SPANNER_EMULATOR_HOST = container.getEmulatorGrpcEndpoint();
  const client = new Spanner({ projectId: container.getProjectId() });

  // list instance configs
  const admin = client.getInstanceAdminClient();
  const [configs] = await admin.listInstanceConfigs({
    parent: admin.projectPath(container.getProjectId()),
  });

  // emulator always includes "emulator-config"
  const expectedConfigName = admin.instanceConfigPath(container.getProjectId(), "emulator-config");
  expect(configs.map((c) => c.name)).toContain(expectedConfigName);
});
it("should create and delete instance and database via helper", async () => {
  await using container = await new SpannerEmulatorContainer(IMAGE).start();
  const helper = new SpannerEmulatorHelper(container);
  const instanceId = "test-instance";
  const databaseId = "test-db";

  // create resources
  await helper.createInstance(instanceId);
  await helper.createDatabase(instanceId, databaseId);

  const client = helper.client;

  // verify instance exists
  const [instanceExists] = await client.instance(instanceId).exists();
  expect(instanceExists).toBe(true);

  // verify database exists
  const [dbExists] = await client.instance(instanceId).database(databaseId).exists();
  expect(dbExists).toBe(true);

  // delete resources
  await helper.deleteDatabase(instanceId, databaseId);
  await helper.deleteInstance(instanceId);

  // verify deletions
  const [dbExistsAfter] = await client.instance(instanceId).database(databaseId).exists();
  expect(dbExistsAfter).toBe(false);

  const [instanceExistsAfter] = await client.instance(instanceId).exists();
  expect(instanceExistsAfter).toBe(false);
});