Skip to content

Commit 6d16853

Browse files
authored
Testcontainers Support in WireMock Python (#72)
Adds support for testcontainers-python. Now you can programmatically spin up a `WireMockContainer` directly from your test suite. WireMockContainer ships with support for auto generating mapping and stub files, easily manage the container life cycle with the `wiremock_container` context manager, integrates easily into your existing test suite, supports docker in docker mode for common CI use cases and much more.
1 parent 154ad81 commit 6d16853

File tree

17 files changed

+1486
-67
lines changed

17 files changed

+1486
-67
lines changed

.flake8

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[flake8]
2+
ignore = E226,E302,E41
3+
max-line-length = 88
4+
max-complexity = 10

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
- name: Install dependencies
2828
run: |
2929
poetry env use ${{matrix.python-version}}
30-
poetry install
30+
poetry install --extras=testing
3131
3232
- name: Test with pytest
3333
run: |

README.md

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,86 @@
66
</a>
77
</p>
88

9-
This is a python admin API client to a standalone WireMock server.
9+
Python Wiremock is an HTTP client that allows users to interact with a Wiremock instance from within a Python project.
1010

1111
[![a](https://img.shields.io/badge/slack-%23wiremock%2Fpython-brightgreen?style=flat&logo=slack)](https://slack.wiremock.org/)
1212
[![Coverage Status](https://coveralls.io/repos/github/wiremock/python-wiremock/badge.svg?branch=master)](https://coveralls.io/github/wiremock/python-wiremock?branch=master)
1313
[![Docs](https://img.shields.io/badge/docs-latest-brightgreen.svg)](http://wiremock.readthedocs.org/)
1414

15+
## Key Features
16+
17+
WireMock can run in unit tests, as a standalone process or a container. Key features include:
18+
19+
- Supports most of the major [Wiremock](https://wiremock.org/docs) features (more on their way soon)
20+
- Support for [testcontainers-python](https://github.com/testcontainers/testcontainers-python) to easily start wiremock server for your tests
21+
- Support for standalone wiremock JAVA sever
22+
1523
## Install as Dependency
1624

1725
To install:
1826

19-
pip install wiremock
27+
`pip install wiremock`
28+
29+
To install with testing dependencies:
30+
31+
`pip install wiremock[testing]`
32+
33+
To install via Poetry:
34+
35+
`poetry add --extras=testing wiremock`
36+
37+
## Quick Start
38+
39+
The preferred way of using WireMock to mock your services is by using the provided `WireMockContainer` [testcontainers-python](https://github.com/testcontainers/testcontainers-python).
40+
41+
```python
42+
import pytest
43+
44+
from wiremock.testing.testcontainer import wiremock_container
45+
46+
@pytest.fixture(scope="session") # (1)
47+
def wm_server():
48+
with wiremock_container(secure=False) as wm:
49+
50+
Config.base_url = wm.get_url("__admin") # (2)
51+
52+
Mappings.create_mapping(
53+
Mapping(
54+
request=MappingRequest(method=HttpMethods.GET, url="/hello"),
55+
response=MappingResponse(status=200, body="hello"),
56+
persistent=False,
57+
)
58+
) # (3)
59+
yield wm
60+
61+
62+
def test_get_hello_world(wm_server): # (4)
63+
64+
resp1 = requests.get(wm_server.get_url("/hello"), verify=False)
65+
66+
assert resp1.status_code == 200
67+
assert resp1.content == b"hello"
68+
```
69+
70+
1. Create a pytest fixture to manage the container life-cycle. use fixture `scope` to control how often the container is created
71+
72+
2. Set the wiremock sdk config url to the url exposed by the container
73+
74+
3. Create response and request mappings using the Admin SDK.
75+
76+
4. Use the `wm_server` fixture in your tests and make requests against the mock server.
77+
78+
You can read more about Testcontainers support in python-wiremock [here](docs/testcontainers.md).
79+
80+
## Examples
81+
82+
There are several example projects included to demonstrate the different ways that wiremock can be used to mock
83+
services in your tests and systems. The example test modules demonstrate different strategies for testing against
84+
the same "product service" and act as a good demonstration of real world applications to help you get started.
85+
86+
- [Python Testcontainers](examples/tests/test_containers.py)
87+
88+
- [Standlone JAVA Server Version](examples/tests/test_java_server.py)
2089

2190
## Documentation
2291

docs/testcontainers.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# WireMock module for Testcontainers Python
2+
3+
Python WireMock ships with support for [testcontainers-wiremock](https://github.com/testcontainers/testcontainers-python) to easily start WireMock server from your test suite using Python.
4+
5+
## Using the context manager
6+
7+
The simplest way to integrate the WireMock container into your test suite is to use the `wiremock_container` context manager. For pytest users this can be
8+
used in conjuction with a pytest fixture to easily manage the life-cycle of the container.
9+
10+
```python
11+
import pytest
12+
13+
from wiremock.testing.testcontainer import wiremock_container
14+
15+
@pytest.fixture(scope="session") # (1)
16+
def wm_server():
17+
with wiremock_container(secure=False) as wm:
18+
19+
Config.base_url = wm.get_url("__admin") # (2)
20+
21+
Mappings.create_mapping(
22+
Mapping(
23+
request=MappingRequest(method=HttpMethods.GET, url="/hello"),
24+
response=MappingResponse(status=200, body="hello"),
25+
persistent=False,
26+
)
27+
) # (3)
28+
yield wm
29+
30+
31+
def test_get_hello_world(wm_server): # (4)
32+
33+
resp1 = requests.get(wm_server.get_url("/hello"), verify=False)
34+
35+
assert resp1.status_code == 200
36+
assert resp1.content == b"hello"
37+
```
38+
39+
1. Create a pytest fixture to manage the container life-cycle. use fixture `scope` to control how often the container is created
40+
41+
2. Set the wiremock sdk config url to the url exposed by the container
42+
43+
3. Create response and request mappings using the Admin SDK.
44+
45+
4. Use the `wm_server` fixture in your tests and make requests against the mock server.
46+
47+
The context manager will automatically start the container. This is typically what you want as any attempts to generate urls to the contianer when it's not running will result in errors.
48+
49+
If you do need to start the container manually yourself, you can pass `start=False` to the context manager and the context manager will simply yield the instance of the container without starting it.
50+
51+
The `wiremock_container` also supports generating mapping request and response files for you via the mappings kwarg.
52+
53+
```python
54+
55+
@pytest.mark.container_test
56+
def test_configure_via_wiremock_container_context_manager():
57+
58+
mappings = [
59+
(
60+
"hello-world.json",
61+
{
62+
"request": {"method": "GET", "url": "/hello"},
63+
"response": {"status": 200, "body": "hello"},
64+
},
65+
)
66+
]
67+
68+
with wiremock_container(mappings=mappings, verify_ssl_certs=False) as wm:
69+
70+
resp1 = requests.get(wm.get_url("/hello"), verify=False)
71+
assert resp1.status_code == 200
72+
assert resp1.content == b"hello"
73+
```
74+
75+
The `wiremock_container` context manager offers a number of other useful options to help to configure the container. See the `wirewmock.testing.testcontainer.wiremock_container` method for the full description
76+
of options.
77+
78+
## Using the WireMockContainer directly
79+
80+
You can also instantiate the container instance yourself using `WireMockContainer`. The container itself provides methods for creating mapping files and stubs on the container instance which can be used as an alternative
81+
if you maintain your request and response stubs as files.
82+
83+
```python
84+
WireMockContainer(verify_ssl_certs=False)
85+
.with_mapping(
86+
"hello-world.json",
87+
{
88+
"request": {"method": "GET", "url": "/hello"},
89+
"response": {"status": 200, "body": "hello"},
90+
},
91+
)
92+
.with_mapping(
93+
"hello-world-file.json",
94+
{
95+
"request": {"method": "GET", "url": "/hello2"},
96+
"response": {"status": 200, "bodyFileName": "hello.json"},
97+
},
98+
)
99+
.with_file("hello.json", {"message": "Hello World !"})
100+
.with_cli_arg("--verbose", "")
101+
.with_cli_arg("--root-dir", "/home/wiremock")
102+
.with_env("JAVA_OPTS", "-Djava.net.preferIPv4Stack=true")
103+
)
104+
```
105+
106+
## Using WireMockContainer inside Docker (dind)
107+
108+
It's common that you might need to start Testcontainers from inside of another container. The example project in [Testcontainer Example](example/docker-compose.yaml) actually does this.
109+
110+
When running spawning testcontainer inside of another container you will need to set the `WIREMOCK_DIND` config variable to true. When this env var is set the host of the wiremock container
111+
will explicitly be set to `host.docker.internal`.
112+
113+
Let's take a look at the example docker-compose.yaml the example products service uses.
114+
115+
```yaml
116+
version: "3"
117+
118+
services:
119+
overview_srv:
120+
build:
121+
context: ../
122+
dockerfile: example/Dockerfile
123+
ports:
124+
- "5001:5001"
125+
environment:
126+
- WIREMOCK_DIND=true # (1) Set the env var
127+
extra_hosts:
128+
- "host.docker.internal:host-gateway" # (2)
129+
volumes:
130+
- /var/run/docker.sock:/var/run/docker.sock # (3)
131+
- ..:/app/
132+
- .:/app/example/
133+
command: uvicorn product_mock.overview_service:app --host=0.0.0.0 --port=5001
134+
```
135+
136+
1. Set the environment variable to instruct WireMockContainer that we're running in `DIND` mode.
137+
138+
2. Map the host.docker.internal to host-gateway. Docker will magically replace the host-gateway value with the ip of the container.
139+
This mapping is required when using dind on certain CI system like github actions.
140+
141+
3. Mount the docker binary into the container

example/docker-compose.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ services:
77
dockerfile: example/Dockerfile
88
ports:
99
- "5001:5001"
10+
environment:
11+
- WIREMOCK_DIND=true
12+
extra_hosts:
13+
- "host.docker.internal:host-gateway"
1014
volumes:
15+
- /var/run/docker.sock:/var/run/docker.sock
1116
- ..:/app/
1217
- .:/app/example/
1318
command: uvicorn product_mock.overview_service:app --host=0.0.0.0 --port=5001

0 commit comments

Comments
 (0)