Interaction
Interaction module.
This module defines the classes that are used to define individual interactions
within a Pact
between a consumer and a provider. These
interactions can be of different types, such as HTTP requests, synchronous
messages, or asynchronous messages.
An interaction is a specific request that the consumer makes to the provider, and the response that the provider should return. On the consumer side, the interaction clearly defines the request that the consumer will make to the provider and the response that the consumer expects to receive. On the provider side, the interaction is replayed to the provider to ensure that the provider is able to handle the request and return the expected response.
Best Practices¶
When defining an interaction, it is important to ensure that the interaction is as minimal as possible (which is in contrast to the way specifications like OpenAPI are often written). This is because the interaction is used to verify that the consumer and provider can communicate correctly, not to define the entire API.
For example, consider a simple user API that has a GET /user/{id}
endpoint
which returns an object of the form:
{
"id": 123,
"username": "Alice"
"email": "alice@example.com",
"registered": "2021-02-26T10:17:51+11:00",
"last_login": "2024-07-04T13:25:45+10:00"
}
The user client might have two functionalities:
- To check if the user exists, and
- To retrieve the user's username.
The implementation of these two would be:
from pact import Pact
pact = Pact(consumer="UserClient", provider="UserService")
# Checking if a user exists
(
pact.upon_receiving("A request to check if a user exists")
.given("A user with ID 123 exists")
.with_request("GET", "/user/123")
.will_respond_with(200)
)
# Getting a user's username
(
pact.upon_receiving("A request to get a user's username")
.given("A user with ID 123 exists")
.with_request("GET", "/user/123")
.will_respond_with(200)
.with_body({"username": "Alice"})
)
Importantly, even if the server returns more information than just the username, since the client does not care about this information, it should not be included in the interaction.
Classes¶
AsyncMessageInteraction(pact_handle: pact_ffi.PactHandle, description: str)
¶
Bases: Interaction
An asynchronous message interaction.
This class defines an asynchronous message interaction between a consumer and a provider. It defines the kind of messages a consumer can accept, and the is agnostic of the underlying protocol, be it a message queue, Apache Kafka, or some other asynchronous protocol.
Warning
This class is not yet fully implemented and is not yet usable.
This function should not be called directly. Instead, an
AsyncMessageInteraction should be created using the
upon_receiving
method of a
Pact
instance using the "Async"
interaction type.
PARAMETER | DESCRIPTION |
---|---|
pact_handle
|
The Pact instance this interaction belongs to.
TYPE:
|
description
|
Description of the interaction. This must be unique within the Pact.
TYPE:
|
Source code in src/pact/interaction/_async_message_interaction.py
HttpInteraction(pact_handle: pact_ffi.PactHandle, description: str)
¶
Bases: Interaction
A synchronous HTTP interaction.
This class defines a synchronous HTTP interaction between a consumer and a provider. It defines a specific request that the consumer makes to the provider, and the response that the provider should return.
This class provides a simple way to define the request and response for an
HTTP interaction. As many elements are shared between the request and
response, this class provides a common interface for both. The functions
intelligently determine whether the element should be added to the request
or the response based on whether
will_respond_with
has been called.
For example, the following two interactions are equivalent:
(
pact.upon_receiving("a request")
.with_request("GET", "/")
.with_header("X-Foo", "bar")
.will_respond_with(200)
.with_header("X-Hello", "world")
)
(
pact.upon_receiving("a request")
.with_request("GET", "/")
.will_respond_with(200)
.with_header("X-Foo", "bar", part="Request")
.with_header("X-Hello", "world", part="Response")
)
This class should not be instantiated directly. Instead, an
HttpInteraction
should be created using the
upon_receiving
method of a
Pact
instance.
Source code in src/pact/interaction/_http_interaction.py
Functions¶
set_header(name: str, value: str, part: Literal['Request', 'Response'] | None = None) -> Self
¶
Add a header to the request.
Unlike
with_header
, this
function does no additional processing of the header value. This is
useful for headers that contain a JSON object.
PARAMETER | DESCRIPTION |
---|---|
name
|
Name of the header.
TYPE:
|
value
|
Value of the header.
TYPE:
|
part
|
Whether the header should be added to the request or the response. If
TYPE:
|
Source code in src/pact/interaction/_http_interaction.py
set_headers(headers: dict[str, str] | Iterable[tuple[str, str]], part: Literal['Request', 'Response'] | None = None) -> Self
¶
Add multiple headers to the request.
While it is often convenient to use a dictionary to specify headers, this does not support repeated headers. If you need to specify repeated headers, consider one of the following:
-
An alternative dictionary implementation which supports repeated keys such as multidict.
-
Passing in an iterable of key-value tuples.
-
Make multiple calls to this function or
with_header
.
See set_header
for
more information.
PARAMETER | DESCRIPTION |
---|---|
headers
|
Headers to add to the request. |
part
|
Whether the headers should be added to the request or the response. If
TYPE:
|
Source code in src/pact/interaction/_http_interaction.py
will_respond_with(status: int) -> Self
¶
Set the response status.
Ideally, this function is called once all of the request information has
been set. This allows functions such as
with_header
to intelligently determine whether this is a request or response header.
Alternatively, the part
argument can be used to explicitly specify
whether the header should be added to the request or the response.
PARAMETER | DESCRIPTION |
---|---|
status
|
Status for the response.
TYPE:
|
Source code in src/pact/interaction/_http_interaction.py
with_header(name: str, value: str | dict[str, str] | Matcher[object], part: Literal['Request', 'Response'] | None = None) -> Self
¶
Add a header to the request.
If the same header has multiple values (see RFC9110 §5.2), then the same header must be specified multiple times with order being preserved. For example
will expect a request with the following headers:
Note that repeated headers are case insensitive in accordance with RFC 9110 §5.1.
PARAMETER | DESCRIPTION |
---|---|
name
|
Name of the header.
TYPE:
|
value
|
Value of the header. |
part
|
Whether the header should be added to the request or the response. If
TYPE:
|
Source code in src/pact/interaction/_http_interaction.py
with_headers(headers: dict[str, str] | Iterable[tuple[str, str]], part: Literal['Request', 'Response'] | None = None) -> Self
¶
Add multiple headers to the request.
While it is often convenient to use a dictionary to specify headers, this does not support repeated headers. If you need to specify repeated headers, consider one of the following:
-
An alternative dictionary implementation which supports repeated keys such as multidict.
-
Passing in an iterable of key-value tuples.
-
Make multiple calls to this function or
with_header
.
See
with_header
for more information.
PARAMETER | DESCRIPTION |
---|---|
headers
|
Headers to add to the request. |
part
|
Whether the header should be added to the request or the response. If
TYPE:
|
Source code in src/pact/interaction/_http_interaction.py
with_query_parameter(name: str, value: object | Matcher[object]) -> Self
¶
Add a query to the request.
This is the query parameter that the consumer will send to the provider.
If the same parameter can support multiple values, then the same parameter can be specified multiple times:
(
pact.upon_receiving("a request")
.with_query_parameter("name", "John")
.with_query_parameter("name", "Mary")
)
PARAMETER | DESCRIPTION |
---|---|
name
|
Name of the query parameter.
TYPE:
|
value
|
Value of the query parameter. |
Source code in src/pact/interaction/_http_interaction.py
with_query_parameters(parameters: Mapping[str, object | Matcher[object]] | Iterable[tuple[str, object | Matcher[object]]]) -> Self
¶
Add multiple query parameters to the request.
While it is often convenient to use a dictionary to specify query parameters, this does not support repeated keys. If you need to specify repeated keys, consider one of the following:
-
An alternative dictionary implementation which supports repeated keys such as multidict.
-
Passing in an iterable of key-value tuples.
-
Make multiple calls to this function or
with_query_parameter
.
See
with_query_parameter
for more information.
PARAMETER | DESCRIPTION |
---|---|
parameters
|
Query parameters to add to the request.
TYPE:
|
Source code in src/pact/interaction/_http_interaction.py
with_request(method: str, path: str | Matcher[object]) -> Self
¶
Set the request.
This is the request that the consumer will make to the provider.
PARAMETER | DESCRIPTION |
---|---|
method
|
HTTP method for the request.
TYPE:
|
path
|
Path for the request. |
Source code in src/pact/interaction/_http_interaction.py
Interaction(description: str)
¶
Bases: ABC
Interaction between a consumer and a provider.
This abstract class defines an interaction between a consumer and a provider. The concrete subclasses define the type of interaction, and include:
Interaction Part¶
For HTTP and synchronous message interactions, the interaction is split into
two parts: the request and the response. The interaction part is used to
specify which part of the interaction is being set. This is specified using
the part
argument of various methods (which defaults to an intelligent
choice based on the order of the methods called).
The asynchronous message interaction does not have parts, as the interaction contains a single message from the provider (a.ka. the producer of the message) to the consumer. An attempt to set a response part will raise an error.
As this class is abstract, this function should not be called directly but should instead be called through one of the concrete subclasses.
PARAMETER | DESCRIPTION |
---|---|
description
|
Description of the interaction. This must be unique within the Pact.
TYPE:
|
Source code in src/pact/interaction/_base.py
Functions¶
add_text_comment(comment: str) -> Self
¶
Add a text comment for the interaction.
This is used by V4 interactions to set arbitrary text comments for the interaction.
PARAMETER | DESCRIPTION |
---|---|
comment
|
Text of the comment.
TYPE:
|
Warning¶
Internally, the comments are appended to an array under the text
comment key. Care should be taken to ensure that conflicts are not
introduced by set_comment
.
Source code in src/pact/interaction/_base.py
given(state: str, parameters: dict[str, object] | None = None, /, **kwargs: object) -> Self
¶
Set the provider state.
This is the state that the provider should be in when the Interaction is executed. When the provider is being verified, the provider state is passed to the provider so that its internal state can be set to match the provider state.
In its simplest form, the provider state is a string. For example, to
match a provider state of a user exists
, you would use:
In many circumstances, it is useful to parameterize the state with additional data. In the example above, this could be with:
This function can be called repeatedly to specify multiple provider states for the same Interaction. If the same state is specified with different parameters, then the parameters are merged together.
PARAMETER | DESCRIPTION |
---|---|
state
|
Provider state for the Interaction.
TYPE:
|
parameters
|
Should some of the parameters not be valid Python key identifiers, a dictionary can be passed in as the second positional argument. |
kwargs
|
The additional parameters for the provider state, specified as
additional arguments to the function. The values must be
serializable using Python's
TYPE:
|
Source code in src/pact/interaction/_base.py
set_comment(key: str, value: Any | None) -> Self
¶
Set a comment for the interaction.
This is used by V4 interactions to set a comment for the interaction. A comment consists of a key-value pair, where the key is a string and the value is anything that can be encoded as JSON.
PARAMETER | DESCRIPTION |
---|---|
key
|
Key for the comment.
TYPE:
|
value
|
Value for the comment. This must be encodable using
TYPE:
|
Warning¶
This function will overwrite any existing comment with the same key. In
particular, the text
key is used by add_text_comment
.
Source code in src/pact/interaction/_base.py
set_key(key: str | None) -> Self
¶
Sets the key for the interaction.
This is used by V4 interactions to set the key of the interaction, which can subsequently used to reference the interaction.
Source code in src/pact/interaction/_base.py
set_metadata(metadata: dict[str, str] | None = None, part: Literal['Request', 'Response'] | None = None, /, **kwargs: str) -> Self
¶
Add metadata for the interaction.
This function behaves exactly like
with_metadata
but does
not perform any parsing of the value strings. The strings must be valid
JSON-encoded strings.
The value of None
will remove the metadata key from the interaction.
This is distinct from using an empty string or a string containing the
JSON null
value, which will set the metadata key to an empty string
or the JSON null
value, respectively.
PARAMETER | DESCRIPTION |
---|---|
metadata
|
Dictionary of metadata keys and associated values. |
part
|
Whether the metadata should be added to the request or the
response. If
TYPE:
|
**kwargs
|
Additional metadata key-value pairs.
TYPE:
|
RETURNS | DESCRIPTION |
---|---|
Self
|
The current instance of the interaction. |
Source code in src/pact/interaction/_base.py
set_pending(*, pending: bool) -> Self
¶
Mark the interaction as pending.
This is used by V4 interactions to mark the interaction as pending, in which case the provider is not expected to honour the interaction.
Source code in src/pact/interaction/_base.py
test_name(name: str) -> Self
¶
Set the test name annotation for the interaction.
This is used by V4 interactions to set the name of the test.
PARAMETER | DESCRIPTION |
---|---|
name
|
Name of the test.
TYPE:
|
Source code in src/pact/interaction/_base.py
with_binary_body(body: bytes | None, content_type: str | None = None, part: Literal['Request', 'Response'] | None = None) -> Self
¶
Adds a binary body to the request or response.
Note that for HTTP interactions, this function will overwrite the body
if it has been set using
with_body
.
PARAMETER | DESCRIPTION |
---|---|
body
|
Body of the request.
TYPE:
|
content_type
|
Content type of the body. This is ignored if the
TYPE:
|
part
|
Whether the body should be added to the request or the response.
If
TYPE:
|
Source code in src/pact/interaction/_base.py
with_body(body: str | dict[str, Any] | Matcher[Any] | None = None, content_type: str | None = None, part: Literal['Request', 'Response'] | None = None) -> Self
¶
Set the body of the request or response.
PARAMETER | DESCRIPTION |
---|---|
body
|
Body of the request. If this is
TYPE:
|
content_type
|
Content type of the body. This is ignored if the
TYPE:
|
part
|
Whether the body should be added to the request or the response.
If
TYPE:
|
Source code in src/pact/interaction/_base.py
with_generators(generators: dict[str, Any] | str, part: Literal['Request', 'Response'] | None = None) -> Self
¶
Add generators to the interaction.
Generators are used to adjust how parts of the request or response are generated when the Pact is being tested. This can be useful for fields that vary each time the request is made, such as a timestamp.
PARAMETER | DESCRIPTION |
---|---|
generators
|
Generators to add to the interaction. This must be encodable
using |
part
|
Whether the generators should be added to the request or the
response. If
TYPE:
|
Source code in src/pact/interaction/_base.py
with_matching_rules(rules: dict[str, Any] | str, part: Literal['Request', 'Response'] | None = None) -> Self
¶
Add matching rules to the interaction.
Matching rules are used to specify how the request or response should be matched. This is useful for specifying that certain parts of the request or response are flexible, such as the date or time.
PARAMETER | DESCRIPTION |
---|---|
rules
|
Matching rules to add to the interaction. This must be encodable
using |
part
|
Whether the matching rules should be added to the request or the
response. If
TYPE:
|
Source code in src/pact/interaction/_base.py
with_metadata(metadata: dict[str, object | Matcher[object]] | None = None, part: Literal['Request', 'Response'] | None = None, /, **kwargs: object | Matcher[object]) -> Self
¶
Add metadata for the interaction.
This function may either be called with a single dictionary of metadata, or with keyword arguments that are the key-value pairs of the metadata (or a combination thereof):
interaction.with_metadata({"foo": "bar", "baz": "qux"})
interaction.with_metadata(foo="bar", baz="qux")
The value of None
will remove the metadata key from the interaction.
This is distinct from using an empty string or a string containing the
JSON null
value, which will set the metadata key to an empty string or
the JSON null
value, respectively.
The values must be serializable to JSON using json.dumps
and may contain matchers and generators. If you wish to use a valid
JSON-encoded string as a metadata value, prefer the
set_metadata
method as
this does not perform any additional parsing of the string.
PARAMETER | DESCRIPTION |
---|---|
metadata
|
Dictionary of metadata keys and associated values.
TYPE:
|
part
|
Whether the metadata should be added to the request or the
response. If
TYPE:
|
**kwargs
|
Additional metadata key-value pairs. |
RETURNS | DESCRIPTION |
---|---|
Self
|
The current instance of the interaction. |
Source code in src/pact/interaction/_base.py
with_multipart_file(part_name: str, path: Path | None, content_type: str | None = None, part: Literal['Request', 'Response'] | None = None, boundary: str | None = None) -> Self
¶
Adds a binary file as the body of a multipart request or response.
PARAMETER | DESCRIPTION |
---|---|
part_name
|
Name of the multipart part.
TYPE:
|
path
|
Path to the file to add.
TYPE:
|
content_type
|
Content type of the part.
TYPE:
|
part
|
Whether the part should be added to the request or the
response.
If
TYPE:
|
boundary
|
Boundary string for the multipart message.
TYPE:
|
Source code in src/pact/interaction/_base.py
with_plugin_contents(contents: dict[str, Any] | str, content_type: str, part: Literal['Request', 'Response'] | None = None) -> Self
¶
Set the interaction content using a plugin.
The value of contents
is passed directly to the plugin as a JSON
string. The plugin will document the format of the JSON content.
PARAMETER | DESCRIPTION |
---|---|
contents
|
Body of the request. If this is |
content_type
|
Content type of the body. This is ignored if the
TYPE:
|
part
|
Whether the body should be added to the request or the response.
If
TYPE:
|
Source code in src/pact/interaction/_base.py
SyncMessageInteraction(pact_handle: pact_ffi.PactHandle, description: str)
¶
Bases: Interaction
A synchronous message interaction.
This class defines a synchronous message interaction between a consumer and
a provider. As with HttpInteraction
, it
defines a specific request that the consumer makes to the provider, and the
response that the provider should return.
Warning
This class is not yet fully implemented and is not yet usable.
This function should not be called directly. Instead, an
AsyncMessageInteraction should be created using the
upon_receiving
method of a
Pact
instance using the "Sync"
interaction type.
PARAMETER | DESCRIPTION |
---|---|
pact_handle
|
Handle for the Pact.
TYPE:
|
description
|
Description of the interaction. This must be unique within the Pact.
TYPE:
|
Source code in src/pact/interaction/_sync_message_interaction.py
Functions¶
will_respond_with() -> Self
¶
Begin the response part of the interaction.
This method is a convenience method to separate the request and response
parts of the interaction. This function is analogous to the
will_respond_with()
method of the HttpInteraction
class,
albeit more generic for synchronous message interactions.
For example, the following two snippets are equivalent:
Pact(...).upon_receiving("A sync request", interaction="Sync")
.with_body("request body", part="Request")
.with_body("response body", part="Response")
Pact(...).upon_receiving("A sync request", interaction="Sync")
.with_body("request body")
.will_respond_with()
.with_body("response body")
RETURNS | DESCRIPTION |
---|---|
Self
|
The current instance of the interaction. |