Mimicker is a Python-native HTTP mocking server inspired by WireMock, designed to simplify the process of stubbing and mocking HTTP endpoints for testing purposes. Mimicker requires no third-party libraries and is lightweight, making it ideal for integration testing, local development, and CI environments.
- Create HTTP stubs for various endpoints and methods
- Mock responses with specific status codes, headers, and body content
- Flexible configuration for multiple endpoints
Mimicker can be installed directly from PyPI using pip or Poetry:
pip install mimickerpoetry add mimickerTo start Mimicker on a specific port with a simple endpoint, you can use the following code snippet:
from mimicker.mimicker import mimicker, get mimicker(8080).routes( get("/hello"). body({"message": "Hello, World!"}). status(200) )Mimicker can handle path parameters dynamically. Here's how you can mock an endpoint with a variable in the path:
from mimicker.mimicker import mimicker, get mimicker(8080).routes( get("/hello/{name}") .body({"message": "Hello, {name}!"}) .status(200) ) # When the client sends a request to /hello/world, the response will be: # {"message": "Hello, world!"}Mimicker can handle query parameters dynamically. Here's how you can mock an endpoint with a variable in the query:
from mimicker.mimicker import mimicker, get mimicker(8080).routes( get("/hello?name={name}") .body({"message": "Hello, {name}!"}) .status(200) ) # When the client sends a request to /hello?name=world, the response will be: # {"message": "Hello, world!"}When query parameters are not explicitly part of the path template, Mimicker will match all otherwise matching requests against those parameters as if using a wildcard. For instance
from mimicker.mimicker import mimicker, get mimicker(8080).routes( get("/hello") .body({"message": "Hello, world!"}) .status(200) ) # When the client sends a request to /hello?name=bob, the request will match and the response will be: # {"message": "Hello, world!"}These implicitly matched query parameters are available through dynamic responses using the "query_params" key in kwargs in response_func (see below), e.g.
from mimicker.mimicker import mimicker, get def custom_response(**kwargs): query_params: Dict[str, List[str]] = kwargs['query_params'] return 200, {"message": f"Hello {query_params['name'][0]}!"} mimicker(8080).routes( get("/hello") .response_func(custom_response) ) # When the client sends a request to /hello?name=world, the request will match and the response will be: # {"message": "Hello, world!"}Note that because query parameters can be repeated with multiple values, they will always appear in a list of values.
You can also mock responses with custom headers:
from mimicker.mimicker import mimicker, get mimicker(8080).routes( get("/hello") .body("Hello with headers") .headers([("Content-Type", "text/plain"), ("Custom-Header", "Value")]) .status(200) ) # The response will include custom headersMimicker allows you to define multiple routes for different HTTP methods and paths. Here's an example with GET and POST routes:
from mimicker.mimicker import mimicker, get, post mimicker(8080).routes( get("/greet") .body({"message": "Hello, world!"}) .status(200), post("/submit") .body({"result": "Submission received"}) .status(201) ) # Now the server responds to: # GET /greet -> {"message": "Hello, world!"} # POST /submit -> {"result": "Submission received"}You can also mock different HTTP status codes for the same endpoint:
from mimicker.mimicker import mimicker, get mimicker(8080).routes( get("/status") .body({"message": "Success"}) .status(200), get("/error") .body({"message": "Not Found"}) .status(404) ) # GET /status -> {"message": "Success"} with status 200 # GET /error -> {"message": "Not Found"} with status 404Mimicker supports JSON bodies, making it ideal for API testing:
from mimicker.mimicker import mimicker, get mimicker(8080).routes( get("/json") .body({"message": "Hello, JSON!"}) .status(200) ) # The response will be: {"message": "Hello, JSON!"}This is useful when testing how your code handles timeouts when calling a web API.
from mimicker.mimicker import mimicker, get import requests mimicker(8080).routes( get("/wait"). delay(0.5). body("the client should have timed out") ) try: resp = requests.get("http://localhost:8080/wait", timeout=0.2) except requests.exceptions.ReadTimeout as error: print(f"the API is unreachable due to request timeout: {error=}") else: # do things with the response ...In addition to JSON bodies, Mimicker supports other types of content for the response body. Here's how you can return text or file content:
from mimicker.mimicker import mimicker, get mimicker(8080).routes( get("/text") .body("This is a plain text response") .status(200) ) # The response will be plain text: "This is a plain text response"You can also return files from a mock endpoint:
from mimicker.mimicker import mimicker, get mimicker(8080).routes( get("/file") .body(open("example.txt", "rb").read()) # Mock a file response .status(200) ) # The response will be the content of the "example.txt" fileMimicker allows dynamic responses based on the request data using response_func. This feature enables you to build mock responses that adapt based on request parameters, headers, and body.
from mimicker.mimicker import mimicker, post # Available for use with response_func: # kwargs.get("payload") # kwargs.get("headers") # kwargs.get("params") def custom_response(**kwargs): request_payload = kwargs.get("payload") return 200, {"message": f"Hello {request_payload.get('name', 'Guest')}"} mimicker(8080).routes( post("/greet") .response_func(custom_response) ) # POST /greet with body {"name": "World"} -> {"message": "Hello World"} # POST /greet with empty body -> {"message": "Hello Guest"}If you start Mimicker with port 0, the system will automatically assign an available port. You can retrieve the actual port Mimicker is running on using the get_port method:
from mimicker.mimicker import mimicker, get server = mimicker(0).routes( get("/hello") .body({"message": "Hello, World!"}) .status(200) ) actual_port = server.get_port() print(f"Mimicker is running on port {actual_port}")This is useful for test environments where a specific port is not required.
get(path): Defines aGETendpoint.post(path): Defines aPOSTendpoint.put(path): Defines aPUTendpoint.delete(path): Defines aDELETEendpoint.patch(path): Defines aPATCHendpoint..delay(duration): Defines the delay in seconds waited before returning the response (optional, 0. by default)..body(content): Defines the responsebody..status(code): Defines the responsestatus code..headers(headers): Defines responseheaders..response_func(func): Defines a dynamic response function based on the request data.
Mimicker includes built-in logging to help you observe and debug how your mocked endpoints behave.
By default, Mimicker logs at the INFO level and uses a colorized output for readability. You’ll see messages like:
[MIMICKER] [2025-05-04 14:52:10] INFO: ✓ Matched stub. Returning configured response.and:
[MIMICKER] [2025-05-05 11:50:10,226] INFO: → GET /hello Headers: { "host": "localhost:8080", "user-agent": "python-requests/2.31.0", "accept-encoding": "gzip, deflate", "accept": "*/*", "connection": "keep-alive" }You can control the log level using the MIMICKER_LOG_LEVEL environment variable. Valid values include:
DEBUGINFO(default)WARNINGERRORCRITICAL
This will show detailed request information including method, path, headers, and body.
Mimicker supports Python 3.7 and above.
You are welcome to report 🐞 or issues, upvote 👍 feature requests, or 🗣️ discuss features and ideas @ slack community
I'm thankful to all the people who have contributed to this project.
Mimicker is released under the MIT License. see the LICENSE for more information.
