|
2 | 2 | Test Containers
|
3 | 3 | </h1>
|
4 | 4 |
|
5 |
| -Example of using Test containers in Golang. Creating a **redis** cluster with |
6 |
| -containers and test redis cluster with Golang SDK while running unit tests. |
| 5 | +Example of using Test containers in Golang. Creating a **Nats** cluster with |
| 6 | +containers and test nats cluster with Golang SDK while running unit tests. |
| 7 | + |
| 8 | +## What are test-containers? |
| 9 | + |
| 10 | +Test-containers is a package that makes it simple to create and clean up container-based |
| 11 | +dependencies for automated _integration/smoke_ tests. |
7 | 12 |
|
8 | 13 | We use _test-containers_ to run our dependent services like databases in a container
|
9 |
| -hense we won't need to mock them or use real external services. This makes our uint tests more |
10 |
| -efficient and more reliable. Let's see an example. |
| 14 | +hence we won't need to mock them or use real external services. This makes our uint tests more |
| 15 | +efficient and more reliable. |
11 | 16 |
|
12 |
| -## Redis container |
| 17 | +The clean, easy-to-use API enables developers |
| 18 | +to programmatically define containers that should |
| 19 | +be run as part of a test and clean up those |
| 20 | +resources when the test is done. |
13 | 21 |
|
14 |
| -All we need for running a redis container with ```docker-compose``` is |
15 |
| -an image name and image version. |
| 22 | +## Example |
16 | 23 |
|
17 |
| -These data are all in ```pkg/storage/redis/container.go```. |
| 24 | +In the following example, we are going to create a **Nats** |
| 25 | +container and test it. |
18 | 26 |
|
19 | 27 | ```go
|
20 |
| -const ( |
21 |
| - // redis image information |
22 |
| - imageName = "redis" |
23 |
| - imageTag = "latest" |
24 |
| -) |
| 28 | +// container build request |
| 29 | +req := testcontainers.ContainerRequest{ |
| 30 | + Image: "nats:latest", |
| 31 | + ExposedPorts: []string{"4222/tcp", "8222/tcp"}, |
| 32 | + Cmd: []string{ |
| 33 | + "--http_port 8222", |
| 34 | + "--cluster nats://0.0.0.0:6222", |
| 35 | + "--cluster_name NATS", |
| 36 | + }, |
| 37 | + WaitingFor: wait.ForLog("Listening for client connections"), |
| 38 | +} |
25 | 39 | ```
|
26 | 40 |
|
27 |
| -Now we build our container with a request. |
| 41 | +The ```testcontainers.ContainerRequest``` describes how the Docker container will look. |
| 42 | + |
| 43 | +- ```Image``` is the Docker image the container starts from. |
| 44 | +- ```ExposedPorts``` lists the ports to be exposed from the container. |
| 45 | +- ```Cmd``` is the commands that will be executed when container is set. |
| 46 | +- ```WaitingFor``` is a field you can use to validate when a container is ready. It is important to get this set because it helps to know when the container is ready to receive any traffic. In this case, we check for the logs we know come from Redis, telling us that it is ready to accept requests. |
| 47 | + |
| 48 | +When you use ExposedPorts you have to imagine |
| 49 | +yourself using ```docker run -p <port>```. |
| 50 | +When you do so, dockerd maps the selected ```<port>``` from |
| 51 | +inside the container to a random one available on your host. |
28 | 52 |
|
29 | 53 | ```go
|
30 |
| -// container request |
31 |
| -req := testcontainers.ContainerRequest{ |
32 |
| - Image: imageName + ":" + imageTag, |
33 |
| - ExposedPorts: []string{"6379/tcp"}, |
34 |
| - WaitingFor: wait.ForLog("Ready to accept connections"), |
35 |
| -} |
| 54 | +// building a generic container |
| 55 | +container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ |
| 56 | + ContainerRequest: req, |
| 57 | + Started: true, |
| 58 | +}) |
36 | 59 | ```
|
37 | 60 |
|
38 |
| -Now you can access container in your source codes. |
| 61 | +```testcontainers.GenericContainer``` creates the |
| 62 | +container. |
| 63 | +In this example we are using ```Started: true```. |
| 64 | +It means that the container function will wait for |
| 65 | +the container to be up and running. |
| 66 | +If you set the Start value to false it won't start, |
| 67 | +leaving to you the decision about when to start it. |
39 | 68 |
|
40 | 69 | ```go
|
41 |
| -ctx := context.Background() |
| 70 | +// cleaning container after test is complete |
| 71 | +t.Cleanup(func() { |
| 72 | + t.Log("terminating container") |
| 73 | + |
| 74 | + if er := container.Terminate(ctx); er != nil { |
| 75 | + t.Errorf("failed to terminate container: :%v", er) |
| 76 | + } |
| 77 | +}) |
| 78 | +``` |
42 | 79 |
|
43 |
| -// creating a new container. |
44 |
| -redisC, err := redis.CreateRedisContainer() |
45 |
| -if err != nil { |
46 |
| - t.Error(err) |
| 80 | +All the containers must be removed |
| 81 | +at some point, otherwise they will run until |
| 82 | +the host is overloaded. |
| 83 | +One of the ways we have to clean up is by deferring the terminated function: |
| 84 | +```defer container.Terminate(ctx)```. |
47 | 85 |
|
48 |
| - return |
49 |
| -} |
| 86 | +## Talking to container |
50 | 87 |
|
51 |
| -// get container connection. |
52 |
| -redisConnection, err := redisC.Endpoint(ctx, "") |
53 |
| -if err != nil { |
54 |
| - t.Error(err) |
| 88 | +```go |
| 89 | +// opening connection |
| 90 | +nc, er := nats.Connect(container.URI) |
| 91 | +if er != nil { |
| 92 | + t.Error(fmt.Errorf("connecting to nats container failed:\n\t%v\n", er)) |
55 | 93 |
|
56 | 94 | return
|
57 | 95 | }
|
58 |
| -``` |
59 | 96 |
|
60 |
| -## Test |
61 |
| - |
62 |
| -Execute redis container test with following command: |
63 |
| - |
64 |
| -```shell |
65 |
| -go test -v ./... |
66 |
| -``` |
| 97 | +// async subscriber |
| 98 | +go func() { |
| 99 | + _, e := nc.Subscribe(natsTopic, func(m *nats.Msg) { |
| 100 | + log.Printf("Received a message:\n\t%s\n", string(m.Data)) |
| 101 | + }) |
| 102 | + if e != nil { |
| 103 | + t.Error(fmt.Errorf("subscribe over topic failed:\n\t%v\n", e)) |
| 104 | + } |
| 105 | +}() |
| 106 | + |
| 107 | +// publish over topic |
| 108 | +if e := nc.Publish(natsTopic, []byte(natsValue)); e != nil { |
| 109 | + t.Error(fmt.Errorf("publish over topic failed:\n\t%v\n", e)) |
| 110 | +} |
| 111 | +``` |
0 commit comments