testcontainers-mockserver 7.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- testcontainers_mockserver-7.1.0/PKG-INFO +101 -0
- testcontainers_mockserver-7.1.0/README.md +74 -0
- testcontainers_mockserver-7.1.0/pyproject.toml +50 -0
- testcontainers_mockserver-7.1.0/setup.cfg +4 -0
- testcontainers_mockserver-7.1.0/src/testcontainers_mockserver/__init__.py +20 -0
- testcontainers_mockserver-7.1.0/src/testcontainers_mockserver/container.py +139 -0
- testcontainers_mockserver-7.1.0/src/testcontainers_mockserver.egg-info/PKG-INFO +101 -0
- testcontainers_mockserver-7.1.0/src/testcontainers_mockserver.egg-info/SOURCES.txt +11 -0
- testcontainers_mockserver-7.1.0/src/testcontainers_mockserver.egg-info/dependency_links.txt +1 -0
- testcontainers_mockserver-7.1.0/src/testcontainers_mockserver.egg-info/requires.txt +5 -0
- testcontainers_mockserver-7.1.0/src/testcontainers_mockserver.egg-info/top_level.txt +1 -0
- testcontainers_mockserver-7.1.0/tests/test_container_config.py +188 -0
- testcontainers_mockserver-7.1.0/tests/test_integration.py +105 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: testcontainers-mockserver
|
|
3
|
+
Version: 7.1.0
|
|
4
|
+
Summary: Testcontainers module for MockServer
|
|
5
|
+
Author-email: James D Bloom <jamesdbloom@gmail.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://www.mock-server.com
|
|
8
|
+
Project-URL: Repository, https://github.com/mock-server/mockserver
|
|
9
|
+
Project-URL: Documentation, https://www.mock-server.com/mock_server/mockserver_testcontainers.html
|
|
10
|
+
Keywords: testing,docker,mockserver,testcontainers,mocking
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Software Development :: Testing
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: testcontainers>=4.0.0
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
26
|
+
Requires-Dist: requests>=2.20; extra == "test"
|
|
27
|
+
|
|
28
|
+
# testcontainers-mockserver
|
|
29
|
+
|
|
30
|
+
A [Testcontainers](https://testcontainers.com) module for [MockServer](https://www.mock-server.com) in Python.
|
|
31
|
+
|
|
32
|
+
Starts a `mockserver/mockserver` Docker container, waits for readiness, and provides
|
|
33
|
+
convenient accessors for the mapped host, port, and URL.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install testcontainers-mockserver
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from testcontainers_mockserver import MockServerContainer
|
|
45
|
+
import requests
|
|
46
|
+
|
|
47
|
+
with MockServerContainer() as mockserver:
|
|
48
|
+
url = mockserver.get_url() # e.g. "http://localhost:49152"
|
|
49
|
+
|
|
50
|
+
# Create an expectation
|
|
51
|
+
requests.put(f"{url}/mockserver/expectation", json={
|
|
52
|
+
"httpRequest": {"method": "GET", "path": "/hello"},
|
|
53
|
+
"httpResponse": {"statusCode": 200, "body": "world"},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
# Match it
|
|
57
|
+
resp = requests.get(f"{url}/hello")
|
|
58
|
+
assert resp.text == "world"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## API
|
|
62
|
+
|
|
63
|
+
### `MockServerContainer(image=..., port=1080)`
|
|
64
|
+
|
|
65
|
+
- `image` — Docker image to use. Defaults to `mockserver/mockserver:mockserver-7.1.0`.
|
|
66
|
+
- `port` — Container port MockServer listens on. Defaults to `1080`.
|
|
67
|
+
|
|
68
|
+
### Methods
|
|
69
|
+
|
|
70
|
+
| Method | Returns | Description |
|
|
71
|
+
|--------|---------|-------------|
|
|
72
|
+
| `get_url()` | `str` | HTTP base URL (e.g. `http://localhost:49152`) |
|
|
73
|
+
| `get_secure_url()` | `str` | HTTPS base URL (same port, `https://` scheme) |
|
|
74
|
+
| `get_host()` | `str` | Mapped host IP |
|
|
75
|
+
| `get_port()` | `int` | Mapped host port |
|
|
76
|
+
| `with_server_port(port)` | `self` | Override the listen port |
|
|
77
|
+
| `with_log_level(level)` | `self` | Set `MOCKSERVER_LOG_LEVEL` |
|
|
78
|
+
| `with_property(key, value)` | `self` | Set any MockServer env var |
|
|
79
|
+
| `with_initialization_json(path)` | `self` | Point to a startup expectations JSON file |
|
|
80
|
+
|
|
81
|
+
## Building and Testing
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Install in editable mode with test dependencies
|
|
85
|
+
pip install -e .[test]
|
|
86
|
+
|
|
87
|
+
# Run unit tests (no Docker needed)
|
|
88
|
+
pytest tests/test_container_config.py
|
|
89
|
+
|
|
90
|
+
# Run all tests including integration (requires Docker)
|
|
91
|
+
pytest
|
|
92
|
+
|
|
93
|
+
# Skip Docker-dependent tests
|
|
94
|
+
pytest -m "not docker"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Requirements
|
|
98
|
+
|
|
99
|
+
- Python >= 3.9
|
|
100
|
+
- Docker (for integration tests and actual usage)
|
|
101
|
+
- `testcontainers` >= 4.0.0
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# testcontainers-mockserver
|
|
2
|
+
|
|
3
|
+
A [Testcontainers](https://testcontainers.com) module for [MockServer](https://www.mock-server.com) in Python.
|
|
4
|
+
|
|
5
|
+
Starts a `mockserver/mockserver` Docker container, waits for readiness, and provides
|
|
6
|
+
convenient accessors for the mapped host, port, and URL.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install testcontainers-mockserver
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from testcontainers_mockserver import MockServerContainer
|
|
18
|
+
import requests
|
|
19
|
+
|
|
20
|
+
with MockServerContainer() as mockserver:
|
|
21
|
+
url = mockserver.get_url() # e.g. "http://localhost:49152"
|
|
22
|
+
|
|
23
|
+
# Create an expectation
|
|
24
|
+
requests.put(f"{url}/mockserver/expectation", json={
|
|
25
|
+
"httpRequest": {"method": "GET", "path": "/hello"},
|
|
26
|
+
"httpResponse": {"statusCode": 200, "body": "world"},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
# Match it
|
|
30
|
+
resp = requests.get(f"{url}/hello")
|
|
31
|
+
assert resp.text == "world"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## API
|
|
35
|
+
|
|
36
|
+
### `MockServerContainer(image=..., port=1080)`
|
|
37
|
+
|
|
38
|
+
- `image` — Docker image to use. Defaults to `mockserver/mockserver:mockserver-7.1.0`.
|
|
39
|
+
- `port` — Container port MockServer listens on. Defaults to `1080`.
|
|
40
|
+
|
|
41
|
+
### Methods
|
|
42
|
+
|
|
43
|
+
| Method | Returns | Description |
|
|
44
|
+
|--------|---------|-------------|
|
|
45
|
+
| `get_url()` | `str` | HTTP base URL (e.g. `http://localhost:49152`) |
|
|
46
|
+
| `get_secure_url()` | `str` | HTTPS base URL (same port, `https://` scheme) |
|
|
47
|
+
| `get_host()` | `str` | Mapped host IP |
|
|
48
|
+
| `get_port()` | `int` | Mapped host port |
|
|
49
|
+
| `with_server_port(port)` | `self` | Override the listen port |
|
|
50
|
+
| `with_log_level(level)` | `self` | Set `MOCKSERVER_LOG_LEVEL` |
|
|
51
|
+
| `with_property(key, value)` | `self` | Set any MockServer env var |
|
|
52
|
+
| `with_initialization_json(path)` | `self` | Point to a startup expectations JSON file |
|
|
53
|
+
|
|
54
|
+
## Building and Testing
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Install in editable mode with test dependencies
|
|
58
|
+
pip install -e .[test]
|
|
59
|
+
|
|
60
|
+
# Run unit tests (no Docker needed)
|
|
61
|
+
pytest tests/test_container_config.py
|
|
62
|
+
|
|
63
|
+
# Run all tests including integration (requires Docker)
|
|
64
|
+
pytest
|
|
65
|
+
|
|
66
|
+
# Skip Docker-dependent tests
|
|
67
|
+
pytest -m "not docker"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Requirements
|
|
71
|
+
|
|
72
|
+
- Python >= 3.9
|
|
73
|
+
- Docker (for integration tests and actual usage)
|
|
74
|
+
- `testcontainers` >= 4.0.0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "testcontainers-mockserver"
|
|
7
|
+
version = "7.1.0"
|
|
8
|
+
description = "Testcontainers module for MockServer"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "Apache-2.0"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "James D Bloom", email = "jamesdbloom@gmail.com" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["testing", "docker", "mockserver", "testcontainers", "mocking"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
"Topic :: Software Development :: Testing",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"testcontainers>=4.0.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
test = [
|
|
34
|
+
"pytest>=7.0",
|
|
35
|
+
"requests>=2.20",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://www.mock-server.com"
|
|
40
|
+
Repository = "https://github.com/mock-server/mockserver"
|
|
41
|
+
Documentation = "https://www.mock-server.com/mock_server/mockserver_testcontainers.html"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.packages.find]
|
|
44
|
+
where = ["src"]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
testpaths = ["tests"]
|
|
48
|
+
markers = [
|
|
49
|
+
"docker: marks tests that require a running Docker daemon (deselect with '-m \"not docker\"')",
|
|
50
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Testcontainers module for MockServer.
|
|
2
|
+
|
|
3
|
+
Provides a ``MockServerContainer`` that starts a ``mockserver/mockserver`` Docker
|
|
4
|
+
image, waits for readiness, and exposes convenience accessors for the mapped
|
|
5
|
+
host, port, and base URL.
|
|
6
|
+
|
|
7
|
+
Example::
|
|
8
|
+
|
|
9
|
+
from testcontainers_mockserver import MockServerContainer
|
|
10
|
+
|
|
11
|
+
with MockServerContainer() as mockserver:
|
|
12
|
+
url = mockserver.get_url()
|
|
13
|
+
# url is e.g. "http://localhost:49152"
|
|
14
|
+
# Use requests or any HTTP client to interact with MockServer at this URL.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from testcontainers_mockserver.container import MockServerContainer
|
|
18
|
+
|
|
19
|
+
__all__ = ["MockServerContainer"]
|
|
20
|
+
__version__ = "7.0.0"
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""MockServerContainer — Testcontainers module for MockServer.
|
|
2
|
+
|
|
3
|
+
This module provides a thin wrapper around testcontainers' ``DockerContainer`` that
|
|
4
|
+
starts a ``mockserver/mockserver`` image, waits for readiness via the
|
|
5
|
+
``/mockserver/status`` HTTP endpoint (PUT, returns 200), and exposes convenience
|
|
6
|
+
methods for the mapped host, port, and URL.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from testcontainers.core.container import DockerContainer
|
|
14
|
+
from testcontainers.core.wait_strategies import HttpWaitStrategy
|
|
15
|
+
|
|
16
|
+
#: Default port MockServer listens on inside the container (HTTP, HTTPS, SOCKS,
|
|
17
|
+
#: and HTTP CONNECT all served on this single unified port).
|
|
18
|
+
MOCKSERVER_PORT = 1080
|
|
19
|
+
|
|
20
|
+
#: Default Docker image name for MockServer.
|
|
21
|
+
_IMAGE_NAME = "mockserver/mockserver"
|
|
22
|
+
|
|
23
|
+
#: Default image tag, pinned to the current MockServer release version.
|
|
24
|
+
_DEFAULT_TAG = "mockserver-7.0.0"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class MockServerContainer(DockerContainer):
|
|
28
|
+
"""A Testcontainers wrapper that starts a MockServer Docker container.
|
|
29
|
+
|
|
30
|
+
The container starts the ``mockserver/mockserver`` image on port 1080 and waits
|
|
31
|
+
for the ``PUT /mockserver/status`` endpoint to return HTTP 200 before yielding
|
|
32
|
+
control.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
image : str, optional
|
|
37
|
+
Full Docker image reference. Defaults to
|
|
38
|
+
``mockserver/mockserver:mockserver-7.0.0``.
|
|
39
|
+
port : int, optional
|
|
40
|
+
The port MockServer listens on inside the container. Defaults to 1080.
|
|
41
|
+
|
|
42
|
+
Example
|
|
43
|
+
-------
|
|
44
|
+
::
|
|
45
|
+
|
|
46
|
+
from testcontainers_mockserver import MockServerContainer
|
|
47
|
+
|
|
48
|
+
with MockServerContainer() as server:
|
|
49
|
+
url = server.get_url()
|
|
50
|
+
# e.g. "http://localhost:49152"
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
image: str = f"{_IMAGE_NAME}:{_DEFAULT_TAG}",
|
|
56
|
+
port: int = MOCKSERVER_PORT,
|
|
57
|
+
**kwargs,
|
|
58
|
+
) -> None:
|
|
59
|
+
super().__init__(image=image, **kwargs)
|
|
60
|
+
self._port = port
|
|
61
|
+
self.with_exposed_ports(self._port)
|
|
62
|
+
self.with_env("SERVER_PORT", str(self._port))
|
|
63
|
+
|
|
64
|
+
def _build_wait_strategy(self) -> HttpWaitStrategy:
|
|
65
|
+
"""Build the readiness wait strategy.
|
|
66
|
+
|
|
67
|
+
MockServer's ``/mockserver/status`` endpoint requires a PUT request and
|
|
68
|
+
returns HTTP 200 with a JSON body containing ``{ "ports": [...] }`` when
|
|
69
|
+
the server is ready.
|
|
70
|
+
"""
|
|
71
|
+
return (
|
|
72
|
+
HttpWaitStrategy(self._port, "/mockserver/status")
|
|
73
|
+
.with_method("PUT")
|
|
74
|
+
.with_startup_timeout(60)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def start(self) -> "MockServerContainer":
|
|
78
|
+
"""Start the container and wait for MockServer to become ready."""
|
|
79
|
+
super().start()
|
|
80
|
+
self._build_wait_strategy().wait_until_ready(self)
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
# ------------------------------------------------------------------
|
|
84
|
+
# Convenience accessors
|
|
85
|
+
# ------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
def get_host(self) -> str:
|
|
88
|
+
"""Return the host IP to connect to MockServer from the test process."""
|
|
89
|
+
return self.get_container_host_ip()
|
|
90
|
+
|
|
91
|
+
def get_port(self) -> int:
|
|
92
|
+
"""Return the mapped host port for MockServer's container port."""
|
|
93
|
+
return int(self.get_exposed_port(self._port))
|
|
94
|
+
|
|
95
|
+
def get_url(self) -> str:
|
|
96
|
+
"""Return the HTTP base URL for MockServer (e.g. ``http://localhost:49152``)."""
|
|
97
|
+
return f"http://{self.get_host()}:{self.get_port()}"
|
|
98
|
+
|
|
99
|
+
def get_secure_url(self) -> str:
|
|
100
|
+
"""Return the HTTPS base URL for MockServer (same port, different scheme)."""
|
|
101
|
+
return f"https://{self.get_host()}:{self.get_port()}"
|
|
102
|
+
|
|
103
|
+
# ------------------------------------------------------------------
|
|
104
|
+
# Configuration helpers (fluent)
|
|
105
|
+
# ------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
def with_server_port(self, port: int) -> "MockServerContainer":
|
|
108
|
+
"""Override the MockServer listen port inside the container.
|
|
109
|
+
|
|
110
|
+
This replaces the exposed port so the wait strategy targets the correct port.
|
|
111
|
+
"""
|
|
112
|
+
self._port = port
|
|
113
|
+
# Reset exposed ports to only the new port
|
|
114
|
+
self.ports = {}
|
|
115
|
+
self.with_exposed_ports(port)
|
|
116
|
+
self.with_env("SERVER_PORT", str(port))
|
|
117
|
+
return self
|
|
118
|
+
|
|
119
|
+
def with_log_level(self, level: str) -> "MockServerContainer":
|
|
120
|
+
"""Set the MockServer log level (e.g. INFO, DEBUG, WARN, ERROR, TRACE)."""
|
|
121
|
+
self.with_env("MOCKSERVER_LOG_LEVEL", level)
|
|
122
|
+
return self
|
|
123
|
+
|
|
124
|
+
def with_property(self, key: str, value: str) -> "MockServerContainer":
|
|
125
|
+
"""Set a MockServer configuration property as an environment variable.
|
|
126
|
+
|
|
127
|
+
The key must be in MockServer env-var form (e.g. ``MOCKSERVER_LOG_LEVEL``).
|
|
128
|
+
"""
|
|
129
|
+
self.with_env(key, value)
|
|
130
|
+
return self
|
|
131
|
+
|
|
132
|
+
def with_initialization_json(self, container_path: str) -> "MockServerContainer":
|
|
133
|
+
"""Configure MockServer to load expectations from a JSON file at startup.
|
|
134
|
+
|
|
135
|
+
The file must already be mounted/copied into the container at *container_path*.
|
|
136
|
+
This sets the ``MOCKSERVER_INITIALIZATION_JSON_PATH`` environment variable.
|
|
137
|
+
"""
|
|
138
|
+
self.with_env("MOCKSERVER_INITIALIZATION_JSON_PATH", container_path)
|
|
139
|
+
return self
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: testcontainers-mockserver
|
|
3
|
+
Version: 7.1.0
|
|
4
|
+
Summary: Testcontainers module for MockServer
|
|
5
|
+
Author-email: James D Bloom <jamesdbloom@gmail.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://www.mock-server.com
|
|
8
|
+
Project-URL: Repository, https://github.com/mock-server/mockserver
|
|
9
|
+
Project-URL: Documentation, https://www.mock-server.com/mock_server/mockserver_testcontainers.html
|
|
10
|
+
Keywords: testing,docker,mockserver,testcontainers,mocking
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Software Development :: Testing
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: testcontainers>=4.0.0
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
26
|
+
Requires-Dist: requests>=2.20; extra == "test"
|
|
27
|
+
|
|
28
|
+
# testcontainers-mockserver
|
|
29
|
+
|
|
30
|
+
A [Testcontainers](https://testcontainers.com) module for [MockServer](https://www.mock-server.com) in Python.
|
|
31
|
+
|
|
32
|
+
Starts a `mockserver/mockserver` Docker container, waits for readiness, and provides
|
|
33
|
+
convenient accessors for the mapped host, port, and URL.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install testcontainers-mockserver
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from testcontainers_mockserver import MockServerContainer
|
|
45
|
+
import requests
|
|
46
|
+
|
|
47
|
+
with MockServerContainer() as mockserver:
|
|
48
|
+
url = mockserver.get_url() # e.g. "http://localhost:49152"
|
|
49
|
+
|
|
50
|
+
# Create an expectation
|
|
51
|
+
requests.put(f"{url}/mockserver/expectation", json={
|
|
52
|
+
"httpRequest": {"method": "GET", "path": "/hello"},
|
|
53
|
+
"httpResponse": {"statusCode": 200, "body": "world"},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
# Match it
|
|
57
|
+
resp = requests.get(f"{url}/hello")
|
|
58
|
+
assert resp.text == "world"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## API
|
|
62
|
+
|
|
63
|
+
### `MockServerContainer(image=..., port=1080)`
|
|
64
|
+
|
|
65
|
+
- `image` — Docker image to use. Defaults to `mockserver/mockserver:mockserver-7.1.0`.
|
|
66
|
+
- `port` — Container port MockServer listens on. Defaults to `1080`.
|
|
67
|
+
|
|
68
|
+
### Methods
|
|
69
|
+
|
|
70
|
+
| Method | Returns | Description |
|
|
71
|
+
|--------|---------|-------------|
|
|
72
|
+
| `get_url()` | `str` | HTTP base URL (e.g. `http://localhost:49152`) |
|
|
73
|
+
| `get_secure_url()` | `str` | HTTPS base URL (same port, `https://` scheme) |
|
|
74
|
+
| `get_host()` | `str` | Mapped host IP |
|
|
75
|
+
| `get_port()` | `int` | Mapped host port |
|
|
76
|
+
| `with_server_port(port)` | `self` | Override the listen port |
|
|
77
|
+
| `with_log_level(level)` | `self` | Set `MOCKSERVER_LOG_LEVEL` |
|
|
78
|
+
| `with_property(key, value)` | `self` | Set any MockServer env var |
|
|
79
|
+
| `with_initialization_json(path)` | `self` | Point to a startup expectations JSON file |
|
|
80
|
+
|
|
81
|
+
## Building and Testing
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Install in editable mode with test dependencies
|
|
85
|
+
pip install -e .[test]
|
|
86
|
+
|
|
87
|
+
# Run unit tests (no Docker needed)
|
|
88
|
+
pytest tests/test_container_config.py
|
|
89
|
+
|
|
90
|
+
# Run all tests including integration (requires Docker)
|
|
91
|
+
pytest
|
|
92
|
+
|
|
93
|
+
# Skip Docker-dependent tests
|
|
94
|
+
pytest -m "not docker"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Requirements
|
|
98
|
+
|
|
99
|
+
- Python >= 3.9
|
|
100
|
+
- Docker (for integration tests and actual usage)
|
|
101
|
+
- `testcontainers` >= 4.0.0
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/testcontainers_mockserver/__init__.py
|
|
4
|
+
src/testcontainers_mockserver/container.py
|
|
5
|
+
src/testcontainers_mockserver.egg-info/PKG-INFO
|
|
6
|
+
src/testcontainers_mockserver.egg-info/SOURCES.txt
|
|
7
|
+
src/testcontainers_mockserver.egg-info/dependency_links.txt
|
|
8
|
+
src/testcontainers_mockserver.egg-info/requires.txt
|
|
9
|
+
src/testcontainers_mockserver.egg-info/top_level.txt
|
|
10
|
+
tests/test_container_config.py
|
|
11
|
+
tests/test_integration.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
testcontainers_mockserver
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Unit tests for MockServerContainer configuration.
|
|
2
|
+
|
|
3
|
+
These tests verify container configuration WITHOUT starting Docker.
|
|
4
|
+
They test URL shaping, environment variable setup, and port configuration.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from testcontainers_mockserver import MockServerContainer
|
|
10
|
+
from testcontainers_mockserver.container import MOCKSERVER_PORT, _DEFAULT_TAG, _IMAGE_NAME
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestDefaultConfiguration:
|
|
14
|
+
"""Tests for default container configuration (no Docker needed)."""
|
|
15
|
+
|
|
16
|
+
def test_default_image(self):
|
|
17
|
+
container = MockServerContainer()
|
|
18
|
+
assert container.image == f"{_IMAGE_NAME}:{_DEFAULT_TAG}"
|
|
19
|
+
|
|
20
|
+
def test_default_port_constant(self):
|
|
21
|
+
assert MOCKSERVER_PORT == 1080
|
|
22
|
+
|
|
23
|
+
def test_default_exposes_port_1080(self):
|
|
24
|
+
container = MockServerContainer()
|
|
25
|
+
# The ports dict keys are the container ports
|
|
26
|
+
assert MOCKSERVER_PORT in container.ports
|
|
27
|
+
|
|
28
|
+
def test_default_sets_server_port_env(self):
|
|
29
|
+
container = MockServerContainer()
|
|
30
|
+
assert container.env["SERVER_PORT"] == "1080"
|
|
31
|
+
|
|
32
|
+
def test_custom_image(self):
|
|
33
|
+
container = MockServerContainer(image="mockserver/mockserver:latest")
|
|
34
|
+
assert container.image == "mockserver/mockserver:latest"
|
|
35
|
+
|
|
36
|
+
def test_custom_port(self):
|
|
37
|
+
container = MockServerContainer(port=9090)
|
|
38
|
+
assert 9090 in container.ports
|
|
39
|
+
assert container.env["SERVER_PORT"] == "9090"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestWithServerPort:
|
|
43
|
+
"""Tests for with_server_port fluent method."""
|
|
44
|
+
|
|
45
|
+
def test_sets_env_and_port(self):
|
|
46
|
+
container = MockServerContainer()
|
|
47
|
+
result = container.with_server_port(9090)
|
|
48
|
+
|
|
49
|
+
assert result is container # fluent return
|
|
50
|
+
assert container.env["SERVER_PORT"] == "9090"
|
|
51
|
+
assert 9090 in container.ports
|
|
52
|
+
|
|
53
|
+
def test_replaces_default_port(self):
|
|
54
|
+
container = MockServerContainer()
|
|
55
|
+
container.with_server_port(9090)
|
|
56
|
+
|
|
57
|
+
# Default port 1080 should no longer be exposed
|
|
58
|
+
assert MOCKSERVER_PORT not in container.ports
|
|
59
|
+
assert 9090 in container.ports
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class TestWithLogLevel:
|
|
63
|
+
"""Tests for with_log_level fluent method."""
|
|
64
|
+
|
|
65
|
+
def test_sets_log_level_env(self):
|
|
66
|
+
container = MockServerContainer()
|
|
67
|
+
result = container.with_log_level("DEBUG")
|
|
68
|
+
|
|
69
|
+
assert result is container
|
|
70
|
+
assert container.env["MOCKSERVER_LOG_LEVEL"] == "DEBUG"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TestWithProperty:
|
|
74
|
+
"""Tests for with_property fluent method."""
|
|
75
|
+
|
|
76
|
+
def test_sets_arbitrary_env(self):
|
|
77
|
+
container = MockServerContainer()
|
|
78
|
+
result = container.with_property("MOCKSERVER_MAX_EXPECTATIONS", "500")
|
|
79
|
+
|
|
80
|
+
assert result is container
|
|
81
|
+
assert container.env["MOCKSERVER_MAX_EXPECTATIONS"] == "500"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class TestWithInitializationJson:
|
|
85
|
+
"""Tests for with_initialization_json fluent method."""
|
|
86
|
+
|
|
87
|
+
def test_sets_initialization_json_path_env(self):
|
|
88
|
+
container = MockServerContainer()
|
|
89
|
+
result = container.with_initialization_json("/config/init.json")
|
|
90
|
+
|
|
91
|
+
assert result is container
|
|
92
|
+
assert container.env["MOCKSERVER_INITIALIZATION_JSON_PATH"] == "/config/init.json"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class TestFluentChaining:
|
|
96
|
+
"""Tests for method chaining across multiple fluent helpers."""
|
|
97
|
+
|
|
98
|
+
def test_chain_multiple_methods(self):
|
|
99
|
+
container = MockServerContainer()
|
|
100
|
+
result = (
|
|
101
|
+
container
|
|
102
|
+
.with_log_level("WARN")
|
|
103
|
+
.with_server_port(8080)
|
|
104
|
+
.with_property("MOCKSERVER_MAX_EXPECTATIONS", "100")
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
assert result is container
|
|
108
|
+
assert container.env["MOCKSERVER_LOG_LEVEL"] == "WARN"
|
|
109
|
+
assert container.env["SERVER_PORT"] == "8080"
|
|
110
|
+
assert container.env["MOCKSERVER_MAX_EXPECTATIONS"] == "100"
|
|
111
|
+
assert 8080 in container.ports
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class TestUrlShaping:
|
|
115
|
+
"""Tests for URL/host/port accessors.
|
|
116
|
+
|
|
117
|
+
These test the URL construction logic by mocking the underlying testcontainers
|
|
118
|
+
methods that require a running container.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
def test_get_url_format(self):
|
|
122
|
+
"""Verify get_url produces the correct format when host and port are known."""
|
|
123
|
+
container = MockServerContainer()
|
|
124
|
+
# Monkey-patch the methods that need Docker
|
|
125
|
+
container.get_container_host_ip = lambda: "127.0.0.1"
|
|
126
|
+
container.get_exposed_port = lambda port: 49152
|
|
127
|
+
|
|
128
|
+
url = container.get_url()
|
|
129
|
+
assert url == "http://127.0.0.1:49152"
|
|
130
|
+
|
|
131
|
+
def test_get_secure_url_format(self):
|
|
132
|
+
"""Verify get_secure_url uses https scheme."""
|
|
133
|
+
container = MockServerContainer()
|
|
134
|
+
container.get_container_host_ip = lambda: "localhost"
|
|
135
|
+
container.get_exposed_port = lambda port: 55000
|
|
136
|
+
|
|
137
|
+
url = container.get_secure_url()
|
|
138
|
+
assert url == "https://localhost:55000"
|
|
139
|
+
|
|
140
|
+
def test_get_host_delegates(self):
|
|
141
|
+
"""Verify get_host delegates to get_container_host_ip."""
|
|
142
|
+
container = MockServerContainer()
|
|
143
|
+
container.get_container_host_ip = lambda: "192.168.1.100"
|
|
144
|
+
|
|
145
|
+
assert container.get_host() == "192.168.1.100"
|
|
146
|
+
|
|
147
|
+
def test_get_port_delegates(self):
|
|
148
|
+
"""Verify get_port delegates to get_exposed_port with the configured port."""
|
|
149
|
+
container = MockServerContainer()
|
|
150
|
+
container.get_exposed_port = lambda port: 32768
|
|
151
|
+
|
|
152
|
+
assert container.get_port() == 32768
|
|
153
|
+
|
|
154
|
+
def test_get_port_respects_custom_port(self):
|
|
155
|
+
"""Verify get_port uses the custom server port when configured."""
|
|
156
|
+
container = MockServerContainer(port=9090)
|
|
157
|
+
|
|
158
|
+
called_with = []
|
|
159
|
+
container.get_exposed_port = lambda port: (called_with.append(port), 32768)[1]
|
|
160
|
+
|
|
161
|
+
container.get_port()
|
|
162
|
+
assert called_with == [9090]
|
|
163
|
+
|
|
164
|
+
def test_get_url_with_custom_port(self):
|
|
165
|
+
"""Verify get_url works correctly after with_server_port."""
|
|
166
|
+
container = MockServerContainer()
|
|
167
|
+
container.with_server_port(9090)
|
|
168
|
+
container.get_container_host_ip = lambda: "localhost"
|
|
169
|
+
|
|
170
|
+
called_with = []
|
|
171
|
+
container.get_exposed_port = lambda port: (called_with.append(port), 45000)[1]
|
|
172
|
+
|
|
173
|
+
url = container.get_url()
|
|
174
|
+
assert url == "http://localhost:45000"
|
|
175
|
+
assert called_with == [9090]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class TestVersionPinning:
|
|
179
|
+
"""Tests that verify the version is correctly pinned."""
|
|
180
|
+
|
|
181
|
+
def test_default_tag_matches_release_version(self):
|
|
182
|
+
"""The default tag must match the current MockServer release."""
|
|
183
|
+
assert _DEFAULT_TAG == "mockserver-7.0.0"
|
|
184
|
+
|
|
185
|
+
def test_module_version(self):
|
|
186
|
+
"""The package version must match the MockServer release."""
|
|
187
|
+
import testcontainers_mockserver
|
|
188
|
+
assert testcontainers_mockserver.__version__ == "7.0.0"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Integration tests for MockServerContainer.
|
|
2
|
+
|
|
3
|
+
These tests require a running Docker daemon. They are skipped when Docker is
|
|
4
|
+
unavailable, mirroring the Java repo's assumeTrue(isDockerAvailable()) convention.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
docker_available = False
|
|
10
|
+
try:
|
|
11
|
+
import docker
|
|
12
|
+
|
|
13
|
+
client = docker.from_env()
|
|
14
|
+
client.ping()
|
|
15
|
+
docker_available = True
|
|
16
|
+
except Exception:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
pytestmark = pytest.mark.docker
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture(scope="module")
|
|
23
|
+
def mockserver():
|
|
24
|
+
"""Start a MockServer container for the module's integration tests."""
|
|
25
|
+
if not docker_available:
|
|
26
|
+
pytest.skip("Docker is not available")
|
|
27
|
+
|
|
28
|
+
from testcontainers_mockserver import MockServerContainer
|
|
29
|
+
|
|
30
|
+
with MockServerContainer() as container:
|
|
31
|
+
yield container
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_container_starts_and_is_reachable(mockserver):
|
|
35
|
+
"""Verify the container starts and the /mockserver/status endpoint responds."""
|
|
36
|
+
import requests
|
|
37
|
+
|
|
38
|
+
url = mockserver.get_url()
|
|
39
|
+
assert url.startswith("http://")
|
|
40
|
+
|
|
41
|
+
# MockServer's status endpoint is PUT-only
|
|
42
|
+
response = requests.put(f"{url}/mockserver/status")
|
|
43
|
+
assert response.status_code == 200
|
|
44
|
+
|
|
45
|
+
body = response.json()
|
|
46
|
+
assert "ports" in body
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_get_host_returns_non_empty(mockserver):
|
|
50
|
+
"""Verify get_host returns a non-empty string."""
|
|
51
|
+
host = mockserver.get_host()
|
|
52
|
+
assert host
|
|
53
|
+
assert isinstance(host, str)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_get_port_returns_positive_int(mockserver):
|
|
57
|
+
"""Verify get_port returns a positive integer."""
|
|
58
|
+
port = mockserver.get_port()
|
|
59
|
+
assert isinstance(port, int)
|
|
60
|
+
assert port > 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_expectation_lifecycle(mockserver):
|
|
64
|
+
"""Verify a basic create-expectation/match/verify lifecycle."""
|
|
65
|
+
import json
|
|
66
|
+
|
|
67
|
+
import requests
|
|
68
|
+
|
|
69
|
+
url = mockserver.get_url()
|
|
70
|
+
|
|
71
|
+
# Create an expectation
|
|
72
|
+
expectation = {
|
|
73
|
+
"httpRequest": {
|
|
74
|
+
"method": "GET",
|
|
75
|
+
"path": "/hello",
|
|
76
|
+
},
|
|
77
|
+
"httpResponse": {
|
|
78
|
+
"statusCode": 200,
|
|
79
|
+
"body": "world",
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
resp = requests.put(f"{url}/mockserver/expectation", json=expectation)
|
|
83
|
+
assert resp.status_code == 201
|
|
84
|
+
|
|
85
|
+
# Match the expectation
|
|
86
|
+
resp = requests.get(f"{url}/hello")
|
|
87
|
+
assert resp.status_code == 200
|
|
88
|
+
assert resp.text == "world"
|
|
89
|
+
|
|
90
|
+
# Verify the request was received
|
|
91
|
+
verify_body = {
|
|
92
|
+
"httpRequest": {
|
|
93
|
+
"method": "GET",
|
|
94
|
+
"path": "/hello",
|
|
95
|
+
},
|
|
96
|
+
"times": {
|
|
97
|
+
"atLeast": 1,
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
resp = requests.put(f"{url}/mockserver/verify", json=verify_body)
|
|
101
|
+
assert resp.status_code == 202
|
|
102
|
+
|
|
103
|
+
# Reset
|
|
104
|
+
resp = requests.put(f"{url}/mockserver/reset")
|
|
105
|
+
assert resp.status_code == 200
|