Welcome to mbtest’s documentation¶
Opinionated Python wrapper & utils for the Mountebank over the wire test double tool.
Includes pytest fixture and PyHamcrest matchers.
Guide¶
(Work in progress)
Use with Docker¶
If you want to use your own mountebank service instance (Docker, for example) you have no need to use npm requirements.
docker run -p 2525:2525 -p IMPOSTER_PORT:IMPOSTER_PORT -d bbyars/mountebank
You can do like this in your [conftest.py
]:
import pytest
from mbtest.server import MountebankServer
@pytest.fixture(scope="session")
def mock_server():
return MountebankServer(port=2525, host="localhost")
Don’t forget to open docker ports for mountebank (default 2525) and for each of its imposters.
from mbtest.imposters import Imposter, Predicate, Response, Stub
imposter = Imposter(
Stub(
Predicate(path="/test") & Predicate(query={}) & Predicate(method="GET"),
Response(body="sausages")
),
record_requests=True,
port=IMPOSTER_PORT)
with mock_server(imposter) as ms:
response = requests.get(f"{imposter.url}/test")
# Check your request
assert_that(imposter, had_request().with_path("/test").and_method("GET"))
If you don’t specify a port for the Imposter it will be allocated randomly.
Extra¶
You can combine your Predicates with &
(and), |
(or).
TODO¶
Basics
Server options
Executing
Existing server, e.g. docker
Running locally, against existing server (e.g. docker)
Stubs, predicates, responses
And and or
Options
Injection
Stubbing vs. Mocking
Assertions and matchers
Proxies
Record/Playback
SMTP
API Reference¶
The mbtest.server module¶
- mbtest.server.mock_server(request, executable=PosixPath('node_modules/.bin/mb'), port=2525, timeout=5, debug=True, allow_injection=True, local_only=True, data_dir='.mbdb')[source]¶
Pytest fixture, making available a mock server, running one or more imposters, one for each domain being mocked.
Use in a pytest conftest.py fixture as follows:
@pytest.fixture(scope="session") def mock_server(request): return server.mock_server(request)
Test will look like:
def test_an_imposter(mock_server): imposter = Imposter(Stub(Predicate(path='/test'), Response(body='sausages')), record_requests=True) with mock_server(imposter) as s: r = requests.get(f"{imposter.url}/test") assert_that(r, is_response().with_status_code(200).and_body("sausages")) assert_that(s, had_request(path='/test', method="GET"))
- Parameters
request (
FixtureRequest
) – Request for a fixture from a test or fixture function.executable (
Union
[str
,Path
]) – Alternate location for the Mountebank executable.port (
int
) – Server port.timeout (
int
) – specifies how long to wait for the Mountebank server to start.debug (
bool
) – Start the server in debug mode, which records all requests. This needs to be True for thembtest.matchers.had_request()
matcher to work.allow_injection (
bool
) – Allow JavaScript injection. If True, local_only should also be True,as per Mountebank security.local_only (
bool
) – Accept request only from localhost.data_dir (
Optional
[str
]) – Persist all operations to disk, in this directory.
- Return type
- Returns
Mock server.
- class mbtest.server.MountebankServer(port, scheme='http', host='localhost', imposters_path='imposters')[source]¶
Allow addition of imposters to an already running Mountebank mock server.
Test will look like:
def test_an_imposter(mock_server): mb = MountebankServer(1234) imposter = Imposter(Stub(Predicate(path='/test'), Response(body='sausages')), record_requests=True) with mb(imposter): r = requests.get(f"{imposter.url}/test") assert_that(r, is_response().with_status_code(200).and_body("sausages")) assert_that(imposter, had_request(path='/test', method="GET"))
Imposters will be torn down when the with block is exited.
- Parameters
- add_impostor(definition)[source]¶
Add single imposter to Mountebank server.
- Parameters
definition – One or more Imposters.
- property server_url: furl.furl.furl¶
- Return type
furl
- query_all_imposters()[source]¶
Yield all imposters running on the server, including those defined elsewhere.
- class mbtest.server.ExecutingMountebankServer(executable=PosixPath('node_modules/.bin/mb'), port=2525, timeout=5, debug=True, allow_injection=True, local_only=True, data_dir='.mbdb')[source]¶
A Mountebank mock server, running one or more imposters, one for each domain being mocked.
Test will look like:
def test_an_imposter(mock_server): mb = ExecutingMountebankServer() imposter = Imposter(Stub(Predicate(path='/test'), Response(body='sausages')), record_requests=True) with mb(imposter) as s: r = requests.get(f"{imposter.url}/test") assert_that(r, is_response().with_status_code(200).and_body("sausages")) assert_that(s, had_request(path='/test', method="GET")) mb.close()
The mountebank server will be started when this class is instantiated, and needs to be closed if it’s not to be left running. Consider using the
mock_server()
pytest fixture, which will take care of this for you.- Parameters
executable (
Union
[str
,Path
]) – Optional, alternate location for the Mountebank executable.port (
int
) – Server port.timeout (
int
) – How long to wait for the Mountebank server to start.debug (
bool
) – Start the server in debug mode, which records all requests. This needs to be True for thembtest.matchers.had_request()
matcher to work.allow_injection (
bool
) –Allow JavaScript injection. If True, local_only should also be True,as per Mountebank security.
local_only (
bool
) – Accept request only from localhost.data_dir (
Optional
[str
]) – Persist all operations to disk, in this directory.
- start_lock = <unlocked _thread.lock object>¶
The mbtest.imposters.imposters module¶
- class mbtest.imposters.imposters.Imposter(stubs, port=None, protocol=Protocol.HTTP, name=None, default_response=None, record_requests=True, mutual_auth=False, key=None, cert=None)[source]¶
Represents a Mountebank imposter. Think of an imposter as a mock website, running a protocol, on a specific port. Required behaviors are specified using stubs.
- Parameters
protocol (
Protocol
) – Protocol to run on.name (
Optional
[str
]) – Imposter name - useful for interactive exploration of imposters on http://localhost:2525/impostersdefault_response (
Optional
[HttpResponse
]) – The default response to send if no predicate matches.record_requests (
bool
) – Record requests made against this imposter, so they can be asserted against later.mutual_auth (
bool
) – Server will request a client certificate.
- class Protocol(value)[source]¶
Imposter Protocol.
- HTTP = 'http'¶
- HTTPS = 'https'¶
- SMTP = 'smtp'¶
- TCP = 'tcp'¶
- property url: furl.furl.furl¶
- Return type
furl
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- property configuration_url: furl.furl.furl¶
- Return type
furl
- query_all_stubs()[source]¶
Return all stubs running on the impostor, including those defined elsewhere.
- class mbtest.imposters.imposters.HttpRequest(method, path, query, headers, body, **kwargs)[source]¶
- class mbtest.imposters.imposters.Address(address, name)¶
- property address¶
Alias for field number 0
- property name¶
Alias for field number 1
The mbtest.imposters.stubs module¶
- class mbtest.imposters.stubs.Stub(predicates=None, responses=None)[source]¶
Represents a Mountebank stub. Think of a stub as a behavior, triggered by a matching predicate.
- Parameters
predicates (
Union
[BasePredicate
,Iterable
[BasePredicate
],None
]) – Trigger this stub if one of these predicates matches the requestresponses (
Union
[BaseResponse
,Iterable
[BaseResponse
],None
]) – Use these response behaviors (in order)
- class mbtest.imposters.stubs.AddStub(stub=None, index=None)[source]¶
Represents a Mountebank add stub request <http://www.mbtest.org/docs/api/overview#add-stub>. To add new stab to an existing imposter.
- Parameters
The mbtest.imposters.predicates module¶
- class mbtest.imposters.predicates.BasePredicate[source]¶
- class mbtest.imposters.predicates.Predicate(path=None, method=None, query=None, body=None, headers=None, xpath=None, operator=Operator.EQUALS, case_sensitive=True)[source]¶
Represents a Mountebank predicate. A predicate can be thought of as a trigger, which may or may not match a request.
- Parameters
- class Method(value)[source]¶
Predicate HTTP method.
- DELETE = 'DELETE'¶
- GET = 'GET'¶
- HEAD = 'HEAD'¶
- POST = 'POST'¶
- PUT = 'PUT'¶
- PATCH = 'PATCH'¶
- class Operator(value)[source]¶
-
- EQUALS = 'equals'¶
- DEEP_EQUALS = 'deepEquals'¶
- CONTAINS = 'contains'¶
- STARTS_WITH = 'startsWith'¶
- ENDS_WITH = 'endsWith'¶
- MATCHES = 'matches'¶
- EXISTS = 'exists'¶
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- class mbtest.imposters.predicates.AndPredicate(left, right)[source]¶
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- class mbtest.imposters.predicates.OrPredicate(left, right)[source]¶
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- class mbtest.imposters.predicates.NotPredicate(inverted)[source]¶
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- class mbtest.imposters.predicates.TcpPredicate(data)[source]¶
Represents a Mountebank TCP predicate. A predicate can be thought of as a trigger, which may or may not match a request.
- Parameters
data (
str
) – Data to match the request.
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- class mbtest.imposters.predicates.InjectionPredicate(inject)[source]¶
Represents a Mountebank injection predicate. A predicate can be thought of as a trigger, which may or may not match a request.
Injection requires Mountebank version 2.0 or higher.
- Parameters
inject (
str
) – JavaScript function to inject.
The mbtest.imposters.responses module¶
- class mbtest.imposters.responses.BaseResponse[source]¶
- class mbtest.imposters.responses.HttpResponse(body='', status_code=200, headers=None, mode=None)[source]¶
Represents a Mountebank HTTP response.
- Parameters
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- class mbtest.imposters.responses.Response(body='', status_code=200, wait=None, repeat=None, headers=None, mode=None, copy=None, decorate=None, lookup=None, shell_transform=None, *, http_response=None)[source]¶
Represents a Mountebank ‘is’ response behavior.
- Parameters
body (
Union
[str
,Any
]) – Body text for response. Can be a string, or a JSON serialisable data structure.wait (
Union
[int
,str
,None
]) – Add latency, in ms.repeat (
Optional
[int
]) – Repeat this many times before moving on to next response.headers (
Optional
[Mapping
[str
,str
]]) – Response HTTP headersdecorate (
Optional
[str
]) – Decorate behavior.shell_transform (
Union
[str
,Iterable
[str
],None
]) – shellTransform behaviorhttp_response (
Optional
[HttpResponse
]) – HTTP Response Fields - use this or the body, status_code, headers and mode fields, not both.
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- property body¶
- property status_code¶
- property headers¶
- property mode¶
- class mbtest.imposters.responses.TcpResponse(data)[source]¶
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- class mbtest.imposters.responses.Proxy(to, wait=None, inject_headers=None, mode=Mode.ONCE, predicate_generators=None, decorate=None)[source]¶
Represents a Mountebank proxy.
- class Mode(value)[source]¶
Defines the replay behavior of the proxy.
- ONCE = 'proxyOnce'¶
- ALWAYS = 'proxyAlways'¶
- TRANSPARENT = 'proxyTransparent'¶
- class mbtest.imposters.responses.PredicateGenerator(path=False, query=False, operator=Operator.EQUALS, case_sensitive=True)[source]¶
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- class mbtest.imposters.responses.InjectionResponse(inject)[source]¶
Represents a Mountebank injection response.
Injection requires Mountebank version 2.0 or higher.
- Parameters
inject (
str
) – JavaScript function to inject .
The mbtest.imposters.behaviors.copy module¶
- class mbtest.imposters.behaviors.copy.Copy(from_, into, using)[source]¶
Represents a copy behavior.
- Parameters
from – The name of the request field to copy from, or, if the request field is an object, then an object specifying the path to the request field.
into (
str
) – The token to replace in the response with the selected request value.using (
Using
) – The configuration needed to select values from the response.
The mbtest.imposters.behaviors.lookup module¶
- class mbtest.imposters.behaviors.lookup.Lookup(key, datasource_path, datasource_key_column, into)[source]¶
Represents a lookup behavior.
- Parameters
The mbtest.imposters.behaviors.using module¶
- class mbtest.imposters.behaviors.using.Using(method, selector)[source]¶
How to select values from the response.
- Parameters
- class Method(value)[source]¶
An enumeration.
- REGEX = 'regex'¶
- XPATH = 'xpath'¶
- JSONPATH = 'jsonpath'¶
- class mbtest.imposters.behaviors.using.UsingRegex(selector, ignore_case=False, multiline=False)[source]¶
Select values from the response using a regular expression.
- Parameters
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- class mbtest.imposters.behaviors.using.UsingXpath(selector, ns=None)[source]¶
Select values from the response using an xpath expression.
- Parameters
- as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
- class mbtest.imposters.behaviors.using.UsingJsonpath(selector)[source]¶
Select values from the response using a jsonpath expression.
- Parameters
selector (
str
) – The selector used to select the value(s) from the request.
The mbtest.matchers module¶
- mbtest.matchers.had_request(method=<hamcrest.core.core.isanything.IsAnything object>, path=<hamcrest.core.core.isanything.IsAnything object>, query=<hamcrest.core.core.isanything.IsAnything object>, headers=<hamcrest.core.core.isanything.IsAnything object>, body=<hamcrest.core.core.isanything.IsAnything object>, times=<hamcrest.core.core.isanything.IsAnything object>)[source]¶
Mountebank server has recorded call matching.
Build criteria with with_ and and_ methods:
assert_that(server, had_request().with_path(“/test”).and_method(“GET”))
Available attributes as per parameters.
- Parameters
method (
Union
[str
,Matcher
[str
]]) – Request’s method matched…path (
Union
[furl
,str
,Matcher
[Union
[furl
,str
]]]) – Request’s path matched…query (
Union
[Mapping
[str
,str
],Matcher
[Mapping
[str
,str
]]]) – Request’s query matched…headers (
Union
[Mapping
[str
,str
],Matcher
[Mapping
[str
,str
]]]) – Request’s headers matched…times (
Union
[int
,Matcher
[int
]]) – Request’s number of times called matched matched…
- Return type
- class mbtest.matchers.HadRequest(method=<hamcrest.core.core.isanything.IsAnything object>, path=<hamcrest.core.core.isanything.IsAnything object>, query=<hamcrest.core.core.isanything.IsAnything object>, headers=<hamcrest.core.core.isanything.IsAnything object>, body=<hamcrest.core.core.isanything.IsAnything object>, times=<hamcrest.core.core.isanything.IsAnything object>)[source]¶
Mountebank server has recorded call matching
- Parameters
method (
Union
[str
,Matcher
[str
]]) – Request’s method matched…path (
Union
[furl
,str
,Matcher
[Union
[furl
,str
]]]) – Request’s path matched…query (
Union
[Mapping
[str
,str
],Matcher
[Mapping
[str
,str
]]]) – Request’s query matched…headers (
Union
[Mapping
[str
,str
],Matcher
[Mapping
[str
,str
]]]) – Request’s headers matched…times (
Union
[int
,Matcher
[int
]]) – Request’s number of times called matched matched…
- describe_to(description)[source]¶
Generates a description of the object.
The description may be part of a description of a larger object of which this is just a component, so it should be worded appropriately.
- Parameters
description (
Description
) – The description to be built or appended to.- Return type
- mbtest.matchers.email_sent(to=<hamcrest.core.core.isanything.IsAnything object>, subject=<hamcrest.core.core.isanything.IsAnything object>, body_text=<hamcrest.core.core.isanything.IsAnything object>)[source]¶
Mountebank SMTP server was asked to sent email matching:
- class mbtest.matchers.EmailSent(to=<hamcrest.core.core.isanything.IsAnything object>, subject=<hamcrest.core.core.isanything.IsAnything object>, body_text=<hamcrest.core.core.isanything.IsAnything object>)[source]¶
Mountebank SMTP server was asked to sent email matching:
- Parameters
- describe_to(description)[source]¶
Generates a description of the object.
The description may be part of a description of a larger object of which this is just a component, so it should be worded appropriately.
- Parameters
description (
Description
) – The description to be built or appended to.- Return type
The mbtest.imposters.base module¶
- class mbtest.imposters.base.JsonSerializable[source]¶
Object capable of being converted to a JSON serializable structure (using
as_structure()
) or from such a structure ((usingfrom_structure()
).- abstract as_structure()[source]¶
Converted to a JSON serializable structure.
- Return type
- Returns
Structure suitable for JSON serialisation.
Indices and tables¶
Installation¶
Install from Pypi as usual, using pip , tox, or setup.py
.
Also requires Mountebank to have been installed:
$ npm install mountebank@2.4 --production
Usage¶
A basic example:
import requests
from hamcrest import assert_that
from brunns.matchers.response import is_response
from mbtest.matchers import had_request
from mbtest.imposters import Imposter, Predicate, Response, Stub
def test_request_to_mock_server(mock_server):
# Set up mock server with required behavior
imposter = Imposter(Stub(Predicate(path="/test"),
Response(body="sausages")))
with mock_server(imposter):
# Make request to mock server - exercise code under test here
response = requests.get(f"{imposter.url}/test")
assert_that("We got the expected response",
response, is_response().with_status_code(200).and_body("sausages"))
assert_that("The mock server recorded the request",
imposter, had_request().with_path("/test").and_method("GET"))
Needs a pytest fixture, most easily defined in conftest.py:
import pytest
from mbtest import server
@pytest.fixture(scope="session")
def mock_server(request):
return server.mock_server(request)