Testcontainers — Node.js / TypeScript
The @floci/testcontainers package integrates Floci with Testcontainers for Node.js. It works with any test runner that supports async/await — Jest, Vitest, Mocha, and others.
Installation
Basic usage — Jest
import { FlociContainer } from "@floci/testcontainers";
import { S3Client, CreateBucketCommand, ListBucketsCommand } from "@aws-sdk/client-s3";
describe("S3", () => {
let floci: FlociContainer;
beforeAll(async () => {
floci = await new FlociContainer().start();
});
afterAll(async () => {
await floci.stop();
});
it("should create and list a bucket", async () => {
const s3 = new S3Client({
endpoint: floci.getEndpoint(),
region: floci.getRegion(),
credentials: {
accessKeyId: floci.getAccessKey(),
secretAccessKey: floci.getSecretKey(),
},
forcePathStyle: true,
});
await s3.send(new CreateBucketCommand({ Bucket: "my-bucket" }));
const { Buckets } = await s3.send(new ListBucketsCommand({}));
expect(Buckets?.some(b => b.Name === "my-bucket")).toBe(true);
});
});
SQS example
import { FlociContainer } from "@floci/testcontainers";
import {
SQSClient,
CreateQueueCommand,
SendMessageCommand,
ReceiveMessageCommand,
} from "@aws-sdk/client-sqs";
describe("SQS", () => {
let floci: FlociContainer;
let sqs: SQSClient;
beforeAll(async () => {
floci = await new FlociContainer().start();
sqs = new SQSClient({
endpoint: floci.getEndpoint(),
region: floci.getRegion(),
credentials: {
accessKeyId: floci.getAccessKey(),
secretAccessKey: floci.getSecretKey(),
},
});
});
afterAll(async () => {
await floci.stop();
});
it("should send and receive a message", async () => {
const { QueueUrl } = await sqs.send(
new CreateQueueCommand({ QueueName: "orders" })
);
await sqs.send(
new SendMessageCommand({
QueueUrl,
MessageBody: JSON.stringify({ event: "order.placed" }),
})
);
const { Messages } = await sqs.send(
new ReceiveMessageCommand({ QueueUrl, MaxNumberOfMessages: 1 })
);
expect(Messages).toHaveLength(1);
expect(JSON.parse(Messages![0].Body!).event).toBe("order.placed");
});
});
DynamoDB example
import { FlociContainer } from "@floci/testcontainers";
import {
DynamoDBClient,
CreateTableCommand,
PutItemCommand,
GetItemCommand,
} from "@aws-sdk/client-dynamodb";
describe("DynamoDB", () => {
let floci: FlociContainer;
let dynamo: DynamoDBClient;
beforeAll(async () => {
floci = await new FlociContainer().start();
dynamo = new DynamoDBClient({
endpoint: floci.getEndpoint(),
region: floci.getRegion(),
credentials: {
accessKeyId: floci.getAccessKey(),
secretAccessKey: floci.getSecretKey(),
},
});
});
afterAll(async () => {
await floci.stop();
});
it("should put and get an item", async () => {
await dynamo.send(
new CreateTableCommand({
TableName: "Orders",
AttributeDefinitions: [{ AttributeName: "id", AttributeType: "S" }],
KeySchema: [{ AttributeName: "id", KeyType: "HASH" }],
BillingMode: "PAY_PER_REQUEST",
})
);
await dynamo.send(
new PutItemCommand({
TableName: "Orders",
Item: {
id: { S: "order-1" },
status: { S: "placed" },
},
})
);
const { Item } = await dynamo.send(
new GetItemCommand({
TableName: "Orders",
Key: { id: { S: "order-1" } },
})
);
expect(Item?.status?.S).toBe("placed");
});
});
Vitest
The same pattern works with Vitest — replace describe/it/expect with their Vitest equivalents (the API is identical):
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { FlociContainer } from "@floci/testcontainers";
import { S3Client, CreateBucketCommand, ListBucketsCommand } from "@aws-sdk/client-s3";
describe("S3", () => {
let floci: FlociContainer;
beforeAll(async () => {
floci = await new FlociContainer().start();
});
afterAll(async () => {
await floci.stop();
});
it("should create a bucket", async () => {
const s3 = new S3Client({
endpoint: floci.getEndpoint(),
region: floci.getRegion(),
credentials: {
accessKeyId: floci.getAccessKey(),
secretAccessKey: floci.getSecretKey(),
},
forcePathStyle: true,
});
await s3.send(new CreateBucketCommand({ Bucket: "vitest-bucket" }));
const { Buckets } = await s3.send(new ListBucketsCommand({}));
expect(Buckets?.some(b => b.Name === "vitest-bucket")).toBe(true);
});
});
Reusing the container across test files
Start the container once in a global setup file and expose the endpoint via an environment variable or a shared module so individual test files don't each start their own container.