Hi everyone! What would be the best way to build ...
# general
g
Hi everyone! What would be the best way to build a test suite that: β€’ uses testcontainers β€’ has only one test container β€’ has many stubs responding different responses to the same requests (some in 200, some in 404, some in 500) Our test data is organized in subdirectories, and ideally, we'd like wiremock to only take into accounts test data from directory
test1
when running the first test, then directory
test2
when running the second test, etc. etc. We are using Golang.
m
Questions 1 and 2 are quite trivial, I've actually done that in go-wiremock's own tests. Check here: https://github.com/wiremock/go-wiremock/blob/main/client_test.go#L23-L49
Reuse
will ensure you're always getting the same container. Note here that testcontainers will, by default, delete containers it created after 10s of inactivity. You can change that behavior with env vars and/or a config file. See the section about ryuk in their doc
Using data from different directories will be up to you. Neither wiremock itself, nor testcontainers can help. You could probably write some function that check what file it's called from, find the directory from that and then load the right data in wiremock. But you'll have to call it manually. Perhaps you could leverage testify's test suites instead to load data per suite in the setup? Just an idea.
g
Hi and thanks for your answer! I've been able to do 1 and 2 as well, it's just 3 in combination with 1 and 2 that causes an issue: right now what I do is I use record and replay, which means I'm not using the stub API at all. Instead, I'm creating json files and copying them into the container, and I was hoping there was some way to tell wiremock to only take into account files from one directory when running a test, then another directory when running another test… maybe using scenarios?
m
I'm no wiremock expert so I might be wrong here (I'm just slowly insering myself on the Golang side!), but I don't think that would be possible by using the same wiremock instance. Maybe you could run different instances of wiremock and for each instance, mount that test's stub directory in the container to
/home/wiremock/mappings
.
There's the
/__admin/mappings/import
endpoint that allows for creating a bunch of stubs at once. Maybe you could leverage that too. Buuuuut, the golang lib doesn't support it yet. That I can fix!
g
What you suggest (having several instances) is something we (I'm not the developer actually doing this) have done, but it was deemed not performant enough. Maybe there are optimizations we could do to make wiremock ready faster and mitigate this issue πŸ€”
m
I'm surprised, starting a new wiremock instance with wiremock barely takes 1s for me
In any case, it would definitely be possible to reuse the one instance. Scenarios could work (not sure, I've never used them). Otherwise, I would just have some util function that loads the mappings necessary for a specific test file and call it as a test setup or something like that.
g
Interesting. When I to run
go test -v
on https://github.com/greg0ire/wiremock-blogpost/tree/bp , the whole test takes 1.86s, and I can feel the 2 steps taking time is: 1. Waiting for the container to be up 2. Stopping the container
Copy code
=== RUN   TestIndexRecord
2025/08/27 15:53:40 github.com/testcontainers/testcontainers-go - Connected to docker:
  Server Version: 28.3.3
  API Version: 1.48
  Operating System: Arch Linux
  Total Memory: 27878 MB
  Testcontainers for Go Version: v0.37.0
  Resolved Docker Host: unix:///var/run/docker.sock
  Resolved Docker Socket Path: /var/run/docker.sock
  Test SessionID: 06357081b125e2a08bc1b947ad1e5f7a014e956d122f8e58c38e21b9b0ba7389
  Test ProcessID: 085b58ee-5919-401e-b86f-680897bdeb88
2025/08/27 15:53:40 🐳 Creating container for image wiremock/wiremock:3.12.1
2025/08/27 15:53:40 🐳 Creating container for image testcontainers/ryuk:0.11.0
2025/08/27 15:53:40 βœ… Container created: b0b6eed28c1f
2025/08/27 15:53:40 🐳 Starting container: b0b6eed28c1f
2025/08/27 15:53:41 βœ… Container started: b0b6eed28c1f
2025/08/27 15:53:41 ⏳ Waiting for container id b0b6eed28c1f image: testcontainers/ryuk:0.11.0. Waiting for: &{Port:8080/tcp timeout:<nil> PollInterval:100ms skipInternalCheck:false}
2025/08/27 15:53:41 πŸ”” Container is ready: b0b6eed28c1f
2025/08/27 15:53:41 βœ… Container created: 3cffcee80267
2025/08/27 15:53:41 🐳 Starting container: 3cffcee80267
2025/08/27 15:53:41 βœ… Container started: 3cffcee80267
2025/08/27 15:53:41 ⏳ Waiting for container id 3cffcee80267 image: wiremock/wiremock:3.12.1. Waiting for: &{timeout:<nil> Port:8080 Path:/__admin StatusCodeMatcher:0x83c5c0 ResponseMatcher:0x896560 UseTLS:false AllowInsecure:false TLSConfig:<nil> Method:GET Body:<nil> Headers:map[] ResponseHeadersMatcher:0x896580 PollInterval:100ms UserInfo: ForceIPv4LocalHost:false}
2025/08/27 15:53:42 πŸ”” Container is ready: 3cffcee80267
2025/08/27 15:53:42 🐳 Stopping container: 3cffcee80267
2025/08/27 15:53:42 βœ… Container stopped: 3cffcee80267
2025/08/27 15:53:42 🐳 Terminating container: 3cffcee80267
2025/08/27 15:53:42 🚫 Container terminated: 3cffcee80267
--- PASS: TestIndexRecord (1.85s)
PASS
ok      algolia-wiremock-testcontainers 1.884s
m
Yeah, that sounds about right, I'm getting similar results
Copy code
$ ALGOLIA_APP_ID=foo ALGOLIA_API_KEY=bar go test -v -count 1 .
=== RUN   TestIndexRecord
2025/08/27 10:07:47 github.com/testcontainers/testcontainers-go - Connected to docker:
  Server Version: 28.3.3
  API Version: 1.50
  Operating System: OrbStack
  Total Memory: 16031 MB
  Testcontainers for Go Version: v0.37.0
  Resolved Docker Host: unix:///var/run/docker.sock
  Resolved Docker Socket Path: /var/run/docker.sock
  Test SessionID: 6d838d47ba19e07bdca0987a8df51fc64443a1191fe12f6ccc61ea00d6f96984
  Test ProcessID: 2879132d-a7e9-4cd5-8868-233efa4bc993
2025/08/27 10:07:47 🐳 Creating container for image wiremock/wiremock:3.12.1
2025/08/27 10:07:47 βœ… Container created: 305f8898133a
2025/08/27 10:07:47 🐳 Starting container: 305f8898133a
2025/08/27 10:07:47 βœ… Container started: 305f8898133a
2025/08/27 10:07:47 ⏳ Waiting for container id 305f8898133a image: wiremock/wiremock:3.12.1. Waiting for: &{timeout:<nil> Port:8080 Path:/__admin StatusCodeMatcher:0x104fa0ad0 ResponseMatcher:0x104fee3e0 UseTLS:false AllowInsecure:false TLSConfig:<nil> Method:GET Body:<nil> Headers:map[] ResponseHeadersMatcher:0x104fee3f0 PollInterval:100ms UserInfo: ForceIPv4LocalHost:false}
2025/08/27 10:07:47 πŸ”” Container is ready: 305f8898133a
2025/08/27 10:07:47 🐳 Stopping container: 305f8898133a
2025/08/27 10:07:48 βœ… Container stopped: 305f8898133a
2025/08/27 10:07:48 🐳 Terminating container: 305f8898133a
2025/08/27 10:07:48 🚫 Container terminated: 305f8898133a
--- PASS: TestIndexRecord (1.25s)
PASS
ok      algolia-wiremock-testcontainers 1.502s
Note that I have
ryuk
disabled so that's probably why it's a bit quicker.
But let's say you have multiple tests in that file, you would reuse the same wiremock instance for all of those tests. In that case, would the ~1.5s not be acceptable?
I've modified your setup with this
Then I copied the test 4 times, just to simulate a "real" test suite. This is the result:
Copy code
=== RUN   TestIndexRecord
2025/08/27 10:33:10 <http://github.com/testcontainers/testcontainers-go|github.com/testcontainers/testcontainers-go> - Connected to docker:
  Server Version: 28.3.3
  API Version: 1.50
  Operating System: OrbStack
  Total Memory: 16031 MB
  Testcontainers for Go Version: v0.37.0
  Resolved Docker Host: unix:///var/run/docker.sock
  Resolved Docker Socket Path: /var/run/docker.sock
  Test SessionID: 025ae1953c93516e0b8c836a7ca9b9d8969d0b8d45a2ed165c4401d0cc44664f
  Test ProcessID: a8ca9de2-fb11-4164-89ef-d96112627b3b
2025/08/27 10:33:10 🐳 Creating container for image wiremock/wiremock:3.12.1
2025/08/27 10:33:10 βœ… Container created: de88eb81855a
2025/08/27 10:33:10 🐳 Starting container: de88eb81855a
2025/08/27 10:33:10 βœ… Container started: de88eb81855a
2025/08/27 10:33:10 ⏳ Waiting for container id de88eb81855a image: wiremock/wiremock:3.12.1. Waiting for: &{timeout:<nil> Port:8080 Path:/__admin StatusCodeMatcher:0x104668460 ResponseMatcher:0x1046b5b10 UseTLS:false AllowInsecure:false TLSConfig:<nil> Method:GET Body:<nil> Headers:map[] ResponseHeadersMatcher:0x1046b5b20 PollInterval:100ms UserInfo: ForceIPv4LocalHost:false}
2025/08/27 10:33:11 πŸ”” Container is ready: de88eb81855a
--- PASS: TestIndexRecord (0.76s)
=== RUN   TestIndexRecord2
2025/08/27 10:33:11 βœ… Container started: de88eb81855a
2025/08/27 10:33:11 ⏳ Waiting for container id de88eb81855a image: wiremock/wiremock:3.12.1. Waiting for: &{timeout:<nil> Port:8080 Path:/__admin StatusCodeMatcher:0x104668460 ResponseMatcher:0x1046b5b10 UseTLS:false AllowInsecure:false TLSConfig:<nil> Method:GET Body:<nil> Headers:map[] ResponseHeadersMatcher:0x1046b5b20 PollInterval:100ms UserInfo: ForceIPv4LocalHost:false}
2025/08/27 10:33:11 πŸ”” Container is ready: de88eb81855a
--- PASS: TestIndexRecord2 (0.14s)
=== RUN   TestIndexRecord3
2025/08/27 10:33:11 βœ… Container started: de88eb81855a
2025/08/27 10:33:11 ⏳ Waiting for container id de88eb81855a image: wiremock/wiremock:3.12.1. Waiting for: &{timeout:<nil> Port:8080 Path:/__admin StatusCodeMatcher:0x104668460 ResponseMatcher:0x1046b5b10 UseTLS:false AllowInsecure:false TLSConfig:<nil> Method:GET Body:<nil> Headers:map[] ResponseHeadersMatcher:0x1046b5b20 PollInterval:100ms UserInfo: ForceIPv4LocalHost:false}
2025/08/27 10:33:11 πŸ”” Container is ready: de88eb81855a
--- PASS: TestIndexRecord3 (0.15s)
=== RUN   TestIndexRecord4
2025/08/27 10:33:11 βœ… Container started: de88eb81855a
2025/08/27 10:33:11 ⏳ Waiting for container id de88eb81855a image: wiremock/wiremock:3.12.1. Waiting for: &{timeout:<nil> Port:8080 Path:/__admin StatusCodeMatcher:0x104668460 ResponseMatcher:0x1046b5b10 UseTLS:false AllowInsecure:false TLSConfig:<nil> Method:GET Body:<nil> Headers:map[] ResponseHeadersMatcher:0x1046b5b20 PollInterval:100ms UserInfo: ForceIPv4LocalHost:false}
2025/08/27 10:33:11 πŸ”” Container is ready: de88eb81855a
--- PASS: TestIndexRecord4 (0.15s)
=== RUN   TestIndexRecord5
2025/08/27 10:33:11 βœ… Container started: de88eb81855a
2025/08/27 10:33:11 ⏳ Waiting for container id de88eb81855a image: wiremock/wiremock:3.12.1. Waiting for: &{timeout:<nil> Port:8080 Path:/__admin StatusCodeMatcher:0x104668460 ResponseMatcher:0x1046b5b10 UseTLS:false AllowInsecure:false TLSConfig:<nil> Method:GET Body:<nil> Headers:map[] ResponseHeadersMatcher:0x1046b5b20 PollInterval:100ms UserInfo: ForceIPv4LocalHost:false}
2025/08/27 10:33:11 πŸ”” Container is ready: de88eb81855a
--- PASS: TestIndexRecord5 (0.16s)
PASS
ok      algolia-wiremock-testcontainers 1.587s
Notice the container is only started once. Now it is not deleted afterwards, but that's what
ryuk
is for. I'm using a hash of
absolutePath
in the container name to ensure that a new container would be created for a different file.
g
🀯
I think my colleague would have a different
absolutePath
for every test, sadly. Will check with them.
m
So in that case really, I would think your best bet would be to start the one instance, keep it running, and just setup the mappings "manually", by using the go-wiremock client.
g
Yeah, my colleague is going to explore that, and also another solution where you start the containers in parallel, but still run the tests sequentially
Ah he's here ! Hi @Antoine Bousquet πŸ™‚
a
πŸ‘‹ I'm the colleague. I've been exploring solutions today and I came to that conclusion as well: 1 container, and use
StubFor
/
Reset
between each test.
another argument for this is that my IDE allows to launch each test separately. If I create a bunch of containers every time it's a bit wasteful.
One thing I'm sad about is that there is no
StubFor
equivalent that could work with a JSON string or a JSON file.
This forces me to do the JSON mapping myself. Unless I should contribute to
go-wiremock
?
g
> This forces me to do the JSON mapping myself. Unless I should contribute to
go-wiremock
? Maybe… is there an API route you could call for that?
Ah yes Mathieu mentioned
/__admin/mappings/import
a
I was thinking of this:
β€’ add a
StufForFromJSON(json string)
that would accept a JSON string and call
StubFor
with the deserialized JSON
β€’ add a
StubForFromJSONFile(jsonPath string)
that would accept a JSON file path and call
StubForFromJSON
with the file content
Since there is already private code for the mapping - only for serialization for now - it could be reused for deserialization
g
You wouldn't have any use for
StubForFromJSON
yourself, right?
a
No, but if we talk about contributing we might as well add it?
g
If you think people will want it, sure. I haven't use the stubs API myself, so I don't know if that's something I would want.