|
| 1 | +--- |
| 2 | +id: testcontainers |
| 3 | +--- |
| 4 | + |
| 5 | +# Testcontainers |
| 6 | + |
| 7 | + |
| 8 | +[](https://gofiber.io/discord) |
| 9 | + |
| 10 | + |
| 11 | +A [Testcontainers](https://golang.testcontainers.org/) Service Implementation for Fiber. |
| 12 | + |
| 13 | +:::note |
| 14 | + |
| 15 | +Requires Go **1.23** and above |
| 16 | + |
| 17 | +::: |
| 18 | + |
| 19 | +## Common Use Cases |
| 20 | + |
| 21 | +- Local development |
| 22 | +- Integration testing |
| 23 | +- Isolated service testing |
| 24 | +- End-to-end testing |
| 25 | + |
| 26 | +## Install |
| 27 | + |
| 28 | +:::caution |
| 29 | + |
| 30 | +This Service Implementation only supports Fiber **v3**. |
| 31 | + |
| 32 | +::: |
| 33 | + |
| 34 | +```shell |
| 35 | +go get -u github.com/gofiber/fiber/v3 |
| 36 | +go get -u github.com/gofiber/contrib/testcontainers |
| 37 | +``` |
| 38 | + |
| 39 | +## Signature |
| 40 | + |
| 41 | +### NewModuleConfig |
| 42 | + |
| 43 | +```go |
| 44 | +// NewModuleConfig creates a new container service config for a module. |
| 45 | +// |
| 46 | +// - The serviceKey is the key used to identify the service in the Fiber app's state. |
| 47 | +// - The img is the image name to use for the container. |
| 48 | +// - The run is the function to use to run the container. It's usually the Run function from the module, like [redis.Run] or [postgres.Run]. |
| 49 | +// - The opts are the functional options to pass to the run function. This argument is optional. |
| 50 | +func NewModuleConfig[T testcontainers.Container]( |
| 51 | + serviceKey string, |
| 52 | + img string, |
| 53 | + run func(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (T, error), |
| 54 | + opts ...testcontainers.ContainerCustomizer, |
| 55 | +) Config[T] { |
| 56 | +``` |
| 57 | +
|
| 58 | +### NewContainerConfig |
| 59 | +
|
| 60 | +```go |
| 61 | +// NewContainerConfig creates a new container service config for a generic container type, |
| 62 | +// not created by a Testcontainers module. So this function best used in combination with |
| 63 | +// the [AddService] function to add a custom container to the Fiber app's state. |
| 64 | +// |
| 65 | +// - The serviceKey is the key used to identify the service in the Fiber app's state. |
| 66 | +// - The img is the image name to use for the container. |
| 67 | +// - The opts are the functional options to pass to the [testcontainers.Run] function. This argument is optional. |
| 68 | +// |
| 69 | +// This function uses the [testcontainers.Run] function as the run function. |
| 70 | +func NewContainerConfig[T *testcontainers.DockerContainer](serviceKey string, img string, opts ...testcontainers.ContainerCustomizer) Config[*testcontainers.DockerContainer] |
| 71 | +``` |
| 72 | +
|
| 73 | +### AddService |
| 74 | +
|
| 75 | +```go |
| 76 | +// AddService adds a Testcontainers container as a [fiber.Service] for the Fiber app. |
| 77 | +// It returns a pointer to a [ContainerService[T]] object, which contains the key used to identify |
| 78 | +// the service in the Fiber app's state, and an error if the config is nil. |
| 79 | +// The container should be a function like redis.Run or postgres.Run that returns a container type |
| 80 | +// which embeds [testcontainers.Container]. |
| 81 | +// - The cfg is the Fiber app's configuration, needed to add the service to the Fiber app's state. |
| 82 | +// - The containerConfig is the configuration for the container, where: |
| 83 | +// - The containerConfig.ServiceKey is the key used to identify the service in the Fiber app's state. |
| 84 | +// - The containerConfig.Run is the function to use to run the container. It's usually the Run function from the module, like redis.Run or postgres.Run. |
| 85 | +// - The containerConfig.Image is the image to use for the container. |
| 86 | +// - The containerConfig.Options are the functional options to pass to the [testcontainers.Run] function. This argument is optional. |
| 87 | +// |
| 88 | +// Use [NewModuleConfig] or [NewContainerConfig] helper functions to create valid containerConfig objects. |
| 89 | +func AddService[T testcontainers.Container](cfg *fiber.Config, containerConfig Config[T]) (*ContainerService[T], error) { |
| 90 | +``` |
| 91 | +
|
| 92 | +## Types |
| 93 | +
|
| 94 | +### Config |
| 95 | +
|
| 96 | +The `Config` type is a generic type that is used to configure the container. |
| 97 | +
|
| 98 | +| Property | Type | Description | Default | |
| 99 | +|-------------|------|-------------|---------| |
| 100 | +| ServiceKey | string | The key used to identify the service in the Fiber app's state. | - | |
| 101 | +| Image | string | The image name to use for the container. | - | |
| 102 | +| Run | func(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (T, error) | The function to use to run the container. It's usually the Run function from the testcontainers-go module, like redis.Run or postgres.Run | - | |
| 103 | +| Options | []testcontainers.ContainerCustomizer | The functional options to pass to the [testcontainers.Run] function. This argument is optional. | - | |
| 104 | +
|
| 105 | +```go |
| 106 | +// Config contains the configuration for a container service. |
| 107 | +type Config[T testcontainers.Container] struct { |
| 108 | + // ServiceKey is the key used to identify the service in the Fiber app's state. |
| 109 | + ServiceKey string |
| 110 | + |
| 111 | + // Image is the image name to use for the container. |
| 112 | + Image string |
| 113 | + |
| 114 | + // Run is the function to use to run the container. |
| 115 | + // It's usually the Run function from the testcontainers-go module, like redis.Run or postgres.Run, |
| 116 | + // although it could be the generic [testcontainers.Run] function from the testcontainers-go package. |
| 117 | + Run func(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (T, error) |
| 118 | + |
| 119 | + // Options are the functional options to pass to the [testcontainers.Run] function. This argument is optional. |
| 120 | + // You can find the available options in the [testcontainers website]. |
| 121 | + // |
| 122 | + // [testcontainers website]: https://golang.testcontainers.org/features/creating_container/#customizing-the-container |
| 123 | + Options []testcontainers.ContainerCustomizer |
| 124 | +} |
| 125 | +``` |
| 126 | +
|
| 127 | +### ContainerService |
| 128 | +
|
| 129 | +The `ContainerService` type is a generic type that embeds a [testcontainers.Container](https://pkg.go.dev/github.com/testcontainers/testcontainers-go#Container) interface, |
| 130 | +and implements the [fiber.Service] interface, thanks to the Start, String, State and Terminate methods. It manages the lifecycle of a `testcontainers.Container` instance, |
| 131 | +and it can be retrieved from the Fiber app's state calling the `fiber.MustGetService` function with the key returned by the `ContainerService.Key` method. |
| 132 | +
|
| 133 | +The type parameter `T` must implement the [testcontainers.Container](https://pkg.go.dev/github.com/testcontainers/testcontainers-go#Container) interface, |
| 134 | +as in the Testcontainers Go modules (e.g. [redis.RedisContainer](https://pkg.go.dev/github.com/testcontainers/testcontainers-go/modules/redis#RedisContainer), |
| 135 | +[postgres.PostgresContainer](https://pkg.go.dev/github.com/testcontainers/testcontainers-go/modules/postgres#PostgresContainer), etc.), or in the generic |
| 136 | +[testcontainers.DockerContainer](https://pkg.go.dev/github.com/testcontainers/testcontainers-go#GenericContainer) type, used for custom containers. |
| 137 | +
|
| 138 | +:::note |
| 139 | +
|
| 140 | +Since `ContainerService` implements the `fiber.Service` interface, container cleanup is handled automatically by the Fiber framework when the application shuts down. There's no need for manual cleanup code. |
| 141 | +
|
| 142 | +::: |
| 143 | +
|
| 144 | +```go |
| 145 | +type ContainerService[T testcontainers.Container] struct |
| 146 | +``` |
| 147 | +
|
| 148 | +#### Signature |
| 149 | +
|
| 150 | +##### Key |
| 151 | +
|
| 152 | +```go |
| 153 | +// Key returns the key used to identify the service in the Fiber app's state. |
| 154 | +// Consumers should use string constants for service keys to ensure consistency |
| 155 | +// when retrieving services from the Fiber app's state. |
| 156 | +func (c *ContainerService[T]) Key() string |
| 157 | +``` |
| 158 | +
|
| 159 | +##### Container |
| 160 | +
|
| 161 | +```go |
| 162 | +// Container returns the Testcontainers container instance, giving full access to the T type methods. |
| 163 | +// It's useful to access the container's methods, like [testcontainers.Container.MappedPort] |
| 164 | +// or [testcontainers.Container.Inspect]. |
| 165 | +func (c *ContainerService[T]) Container() T |
| 166 | +``` |
| 167 | +
|
| 168 | +##### Start |
| 169 | +
|
| 170 | +```go |
| 171 | +// Start creates and starts the container, calling the [run] function with the [img] and [opts] arguments. |
| 172 | +// It implements the [fiber.Service] interface. |
| 173 | +func (c *ContainerService[T]) Start(ctx context.Context) error |
| 174 | +``` |
| 175 | +
|
| 176 | +##### String |
| 177 | +
|
| 178 | +```go |
| 179 | +// String returns the service key, which uniquely identifies the container service. |
| 180 | +// It implements the [fiber.Service] interface. |
| 181 | +func (c *ContainerService[T]) String() string |
| 182 | +``` |
| 183 | +
|
| 184 | +##### State |
| 185 | +
|
| 186 | +```go |
| 187 | +// State returns the status of the container. |
| 188 | +// It implements the [fiber.Service] interface. |
| 189 | +func (c *ContainerService[T]) State(ctx context.Context) (string, error) |
| 190 | +``` |
| 191 | +
|
| 192 | +##### Terminate |
| 193 | +
|
| 194 | +```go |
| 195 | +// Terminate stops and removes the container. It implements the [fiber.Service] interface. |
| 196 | +func (c *ContainerService[T]) Terminate(ctx context.Context) error |
| 197 | +``` |
| 198 | +
|
| 199 | +### Common Errors |
| 200 | +
|
| 201 | +| Error | Description | Resolution | |
| 202 | +|-------|-------------|------------| |
| 203 | +| ErrNilConfig | Returned when the config is nil | Ensure config is properly initialized | |
| 204 | +| ErrContainerNotRunning | Returned when the container is not running | Check container state before operations | |
| 205 | +| ErrEmptyServiceKey | Returned when the service key is empty | Provide a non-empty service key | |
| 206 | +| ErrImageEmpty | Returned when the image is empty | Provide a valid image name | |
| 207 | +| ErrRunNil | Returned when the run is nil | Provide a valid run function | |
| 208 | +
|
| 209 | +## Examples |
| 210 | +
|
| 211 | +You can find more examples in the [testable examples](./examples_test.go). |
| 212 | +
|
| 213 | +### Adding a module container using the Testcontainers Go's Redis module |
| 214 | +
|
| 215 | +```go |
| 216 | +package main |
| 217 | + |
| 218 | +import ( |
| 219 | + "fmt" |
| 220 | + "log" |
| 221 | + |
| 222 | + "github.com/gofiber/fiber/v3" |
| 223 | + |
| 224 | + "github.com/gofiber/contrib/testcontainers" |
| 225 | + tc "github.com/testcontainers/testcontainers-go" |
| 226 | + "github.com/testcontainers/testcontainers-go/modules/redis" |
| 227 | +) |
| 228 | + |
| 229 | +func main() { |
| 230 | + cfg := &fiber.Config{} |
| 231 | + |
| 232 | + // Define the base key for the module service. |
| 233 | + // The service returned by the [testcontainers.AddService] function, |
| 234 | + // using the [ContainerService.Key] method, |
| 235 | + // concatenates the base key with the "using testcontainers-go" suffix. |
| 236 | + const ( |
| 237 | + redisKey = "redis-module" |
| 238 | + ) |
| 239 | + |
| 240 | + // Adding containers coming from the testcontainers-go modules, |
| 241 | + // in this case, a Redis and a Postgres container. |
| 242 | + |
| 243 | + redisModuleConfig := testcontainers.NewModuleConfig(redisKey, "redis:latest", redis.Run) |
| 244 | + redisSrv, err := testcontainers.AddService(cfg, redisModuleConfig) |
| 245 | + if err != nil { |
| 246 | + log.Println("error adding redis module:", err) |
| 247 | + return |
| 248 | + } |
| 249 | + |
| 250 | + // Create a new Fiber app, using the provided configuration. |
| 251 | + app := fiber.New(*cfg) |
| 252 | + |
| 253 | + // Retrieve all services from the app's state. |
| 254 | + // This returns a slice of all the services registered in the app's state. |
| 255 | + srvs := app.State().Services() |
| 256 | + |
| 257 | + // Retrieve the Redis container from the app's state using the key returned by the [ContainerService.Key] method. |
| 258 | + redisCtr := fiber.MustGetService[*testcontainers.ContainerService[*redis.RedisContainer]](app.State(), redisSrv.Key()) |
| 259 | + |
| 260 | + // Start the Fiber app. |
| 261 | + app.Listen(":3000") |
| 262 | +} |
| 263 | +``` |
| 264 | +
|
| 265 | +### Adding a custom container using the Testcontainers Go package |
| 266 | +
|
| 267 | +```go |
| 268 | +package main |
| 269 | + |
| 270 | +import ( |
| 271 | + "fmt" |
| 272 | + "log" |
| 273 | + |
| 274 | + "github.com/gofiber/fiber/v3" |
| 275 | + |
| 276 | + "github.com/gofiber/contrib/testcontainers" |
| 277 | + tc "github.com/testcontainers/testcontainers-go" |
| 278 | +) |
| 279 | + |
| 280 | +func main() { |
| 281 | + cfg := &fiber.Config{} |
| 282 | + |
| 283 | + // Define the base key for the generic service. |
| 284 | + // The service returned by the [testcontainers.AddService] function, |
| 285 | + // using the [ContainerService.Key] method, |
| 286 | + // concatenates the base key with the "using testcontainers-go" suffix. |
| 287 | + const ( |
| 288 | + nginxKey = "nginx-generic" |
| 289 | + ) |
| 290 | + |
| 291 | + // Adding a generic container, directly from the testcontainers-go package. |
| 292 | + containerConfig := testcontainers.NewContainerConfig(nginxKey, "nginx:latest", tc.WithExposedPorts("80/tcp")) |
| 293 | + |
| 294 | + nginxSrv, err := testcontainers.AddService(cfg, containerConfig) |
| 295 | + if err != nil { |
| 296 | + log.Println("error adding nginx generic:", err) |
| 297 | + return |
| 298 | + } |
| 299 | + |
| 300 | + app := fiber.New(*cfg) |
| 301 | + |
| 302 | + nginxCtr := fiber.MustGetService[*testcontainers.ContainerService[*tc.DockerContainer]](app.State(), nginxSrv.Key()) |
| 303 | + |
| 304 | + // Start the Fiber app. |
| 305 | + app.Listen(":3000") |
| 306 | +} |
| 307 | +``` |
0 commit comments