# Testcontainers — Go The `testcontainers-floci-go` module integrates Floci with [Testcontainers for Go](https://golang.testcontainers.org/). It starts a real Floci container before your tests and shuts it down after, with no extra setup. ## Installation ```bash go get github.com/floci-io/testcontainers-floci-go ``` Requires Go 1.25+ and Testcontainers for Go v0.42+. ## Basic usage ```go package myservice_test import ( "context" "testing" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/s3" floci "github.com/floci-io/testcontainers-floci-go" ) func TestS3CreateBucket(t *testing.T) { ctx := context.Background() container, err := floci.NewFlociContainer().Start(ctx) if err != nil { t.Fatal(err) } defer container.Stop(ctx) cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(container.GetRegion()), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( container.GetAccessKey(), container.GetSecretKey(), "", )), config.WithBaseEndpoint(container.GetEndpoint()), ) if err != nil { t.Fatal(err) } client := s3.NewFromConfig(cfg, func(o *s3.Options) { o.UsePathStyle = true }) _, err = client.CreateBucket(ctx, &s3.CreateBucketInput{ Bucket: aws.String("my-bucket"), }) if err != nil { t.Fatal(err) } out, err := client.ListBuckets(ctx, &s3.ListBucketsInput{}) if err != nil { t.Fatal(err) } var found bool for _, b := range out.Buckets { if aws.ToString(b.Name) == "my-bucket" { found = true break } } if !found { t.Error("bucket not found after create") } } ``` ## SQS example ```go func TestSqsSendReceive(t *testing.T) { ctx := context.Background() container, err := floci.NewFlociContainer().Start(ctx) if err != nil { t.Fatal(err) } defer container.Stop(ctx) cfg, _ := config.LoadDefaultConfig(ctx, config.WithRegion(container.GetRegion()), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( container.GetAccessKey(), container.GetSecretKey(), "", )), config.WithBaseEndpoint(container.GetEndpoint()), ) client := sqs.NewFromConfig(cfg) queue, err := client.CreateQueue(ctx, &sqs.CreateQueueInput{ QueueName: aws.String("orders"), }) if err != nil { t.Fatal(err) } _, err = client.SendMessage(ctx, &sqs.SendMessageInput{ QueueUrl: queue.QueueUrl, MessageBody: aws.String(`{"event":"order.placed"}`), }) if err != nil { t.Fatal(err) } out, err := client.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{ QueueUrl: queue.QueueUrl, MaxNumberOfMessages: 1, }) if err != nil { t.Fatal(err) } if len(out.Messages) != 1 { t.Fatalf("expected 1 message, got %d", len(out.Messages)) } } ``` ## DynamoDB example ```go func TestDynamoDBPutGet(t *testing.T) { ctx := context.Background() container, err := floci.NewFlociContainer().Start(ctx) if err != nil { t.Fatal(err) } defer container.Stop(ctx) cfg, _ := config.LoadDefaultConfig(ctx, config.WithRegion(container.GetRegion()), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( container.GetAccessKey(), container.GetSecretKey(), "", )), config.WithBaseEndpoint(container.GetEndpoint()), ) client := dynamodb.NewFromConfig(cfg) _, err = client.CreateTable(ctx, &dynamodb.CreateTableInput{ TableName: aws.String("Orders"), BillingMode: types.BillingModePayPerRequest, AttributeDefinitions: []types.AttributeDefinition{ {AttributeName: aws.String("id"), AttributeType: types.ScalarAttributeTypeS}, }, KeySchema: []types.KeySchemaElement{ {AttributeName: aws.String("id"), KeyType: types.KeyTypeHash}, }, }) if err != nil { t.Fatal(err) } _, err = client.PutItem(ctx, &dynamodb.PutItemInput{ TableName: aws.String("Orders"), Item: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: "order-1"}, "status": &types.AttributeValueMemberS{Value: "placed"}, }, }) if err != nil { t.Fatal(err) } result, err := client.GetItem(ctx, &dynamodb.GetItemInput{ TableName: aws.String("Orders"), Key: map[string]types.AttributeValue{ "id": &types.AttributeValueMemberS{Value: "order-1"}, }, }) if err != nil { t.Fatal(err) } status := result.Item["status"].(*types.AttributeValueMemberS).Value if status != "placed" { t.Errorf("expected status 'placed', got '%s'", status) } } ``` ## Reusing the container across tests Start the container once with `TestMain` and share it across all tests in the package: ```go var sharedContainer *floci.StartedFlociContainer func TestMain(m *testing.M) { ctx := context.Background() var err error sharedContainer, err = floci.NewFlociContainer().Start(ctx) if err != nil { log.Fatalf("failed to start floci: %v", err) } code := m.Run() sharedContainer.Stop(ctx) os.Exit(code) } func awsCfg(ctx context.Context) aws.Config { cfg, _ := config.LoadDefaultConfig(ctx, config.WithRegion(sharedContainer.GetRegion()), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( sharedContainer.GetAccessKey(), sharedContainer.GetSecretKey(), "", )), config.WithBaseEndpoint(sharedContainer.GetEndpoint()), ) return cfg } ``` ## Options Options are chained on the builder before calling `Start`: ```go container, err := floci.NewFlociContainer(). WithRegion("eu-west-1"). WithAccountID("123456789012"). WithImage("floci/floci:1.6.0"). WithDedicatedNetwork(). Start(ctx) ``` | Method | Default | Description | |---|---|---| | `WithImage(image string)` | `floci/floci:latest` | Docker image to use | | `WithRegion(region string)` | `us-east-1` | AWS region set in Floci and returned by `GetRegion()` | | `WithAccountID(id string)` | `000000000000` | Default AWS account ID used in ARNs | | `WithAvailabilityZone(az string)` | `us-east-1a` | Availability zone reported by Floci | | `WithDedicatedNetwork()` | _(none)_ | Create a dedicated Docker network for container-backed services (ElastiCache, RDS, OpenSearch, MSK) | ## Service configuration Each service exposes a typed config struct. Pass it with the matching `WithConfig` method: ```go container, err := floci.NewFlociContainer(). WithS3Config(floci.S3Config{ Enabled: true, DefaultPresignExpirySeconds: 7200, }). WithSqsConfig(floci.SqsConfig{ Enabled: true, DefaultVisibilityTimeout: 60, MaxMessageSize: 131072, }). WithDynamoDbConfig(floci.DynamoDbConfig{ Enabled: true, }). Start(ctx) ``` Available config methods: `WithAcmConfig`, `WithApiGatewayConfig`, `WithAppConfigConfig`, `WithAthenaConfig`, `WithBackupConfig`, `WithBedrockRuntimeConfig`, `WithCloudFormationConfig`, `WithCloudWatchLogsConfig`, `WithCloudWatchMetricsConfig`, `WithCodeBuildConfig`, `WithCodeDeployConfig`, `WithCognitoConfig`, `WithDynamoDbConfig`, `WithEc2Config`, `WithEcrConfig`, `WithEcsConfig`, `WithEksConfig`, `WithElastiCacheConfig`, `WithElbConfig`, `WithEventBridgeConfig`, `WithFirehoseConfig`, `WithGlueConfig`, `WithIamConfig`, `WithKinesisConfig`, `WithKmsConfig`, `WithLambdaConfig`, `WithMskConfig`, `WithOpenSearchConfig`, `WithRdsConfig`, `WithRoute53Config`, `WithS3Config`, `WithSchedulerConfig`, `WithSecretsManagerConfig`, `WithSesConfig`, `WithSnsConfig`, `WithSqsConfig`, `WithSsmConfig`, `WithStepFunctionsConfig`, `WithStsConfig`, `WithTextractConfig`, `WithTransferConfig`. ## `StartedFlociContainer` API | Method | Returns | Description | |---|---|---| | `GetEndpoint()` | `string` | Full HTTP endpoint, e.g. `http://localhost:32768` | | `GetRegion()` | `string` | AWS region configured at start | | `GetAccountID()` | `string` | AWS account ID configured at start | | `GetAccessKey()` | `string` | Always `"test"` | | `GetSecretKey()` | `string` | Always `"test"` | | `GetAvailabilityZone()` | `string` | Availability zone configured at start | | `GetDedicatedNetworkName()` | `string` | Docker network name, or empty string if not using a dedicated network | | `GetMappedPort(ctx, port int)` | `(int, error)` | Host port mapped to the given container port | | `Stop(ctx)` | `error` | Terminate the container and clean up | ## Source [github.com/floci-io/testcontainers-floci-go](https://github.com/floci-io/testcontainers-floci-go)