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);
});