Skip to content

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:

  1. To check if the user exists, and
  2. 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: PactHandle

description

Description of the interaction. This must be unique within the Pact.

TYPE: str

Source code in src/pact/interaction/_async_message_interaction.py
def __init__(self, pact_handle: pact_ffi.PactHandle, description: str) -> None:
    """
    Initialise a new Asynchronous Message Interaction.

    This function should not be called directly. Instead, an
    AsyncMessageInteraction should be created using the
    [`upon_receiving`][pact.Pact.upon_receiving] method of a
    [`Pact`][pact.Pact] instance using the `"Async"` interaction type.

    Args:
        pact_handle:
            The Pact instance this interaction belongs to.

        description:
            Description of the interaction. This must be unique within the
            Pact.
    """
    super().__init__(description)
    self.__handle = pact_ffi.new_message_interaction(pact_handle, description)

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
def __init__(self, pact_handle: pact_ffi.PactHandle, description: str) -> None:
    """
    Initialise a new HTTP Interaction.

    This class should not be instantiated directly. Instead, an
    `HttpInteraction` should be created using the
    [`upon_receiving`][pact.Pact.upon_receiving] method of a
    [`Pact`][pact.Pact] instance.
    """
    super().__init__(description)
    self.__handle = pact_ffi.new_interaction(pact_handle, description)
    self.__interaction_part = pact_ffi.InteractionPart.REQUEST
    self._request_indices: dict[
        tuple[pact_ffi.InteractionPart, str],
        int,
    ] = defaultdict(int)
    self._parameter_indices: dict[str, int] = defaultdict(int)

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: str

value

Value of the header.

TYPE: str

part

Whether the header should be added to the request or the response.

If None, then the function intelligently determines whether the header should be added to the request or the response, based on whether the will_respond_with method has been called.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/interaction/_http_interaction.py
def set_header(
    self,
    name: str,
    value: str,
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    r"""
    Add a header to the request.

    Unlike
    [`with_header`][pact.interaction.HttpInteraction.with_header], this
    function does no additional processing of the header value. This is
    useful for headers that contain a JSON object.

    Args:
        name:
            Name of the header.

        value:
            Value of the header.

        part:
            Whether the header should be added to the request or the
            response.

            If `None`, then the function intelligently determines whether
            the header should be added to the request or the response, based
            on whether the
            [`will_respond_with`][pact.interaction.HttpInteraction.will_respond_with]
            method has been called.
    """
    pact_ffi.set_header(
        self._handle,
        self._parse_interaction_part(part),
        name,
        value,
    )
    return self
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.

TYPE: dict[str, str] | Iterable[tuple[str, str]]

part

Whether the headers should be added to the request or the response.

If None, then the function intelligently determines whether the headers should be added to the request or the response, based on whether the will_respond_with method has been called.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/interaction/_http_interaction.py
def set_headers(
    self,
    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](https://pypi.org/project/multidict/).

    -   Passing in an iterable of key-value tuples.

    -   Make multiple calls to this function or
        [`with_header`][pact.interaction.HttpInteraction.with_header].

    See [`set_header`][pact.interaction.HttpInteraction.set_header] for
    more information.

    Args:
        headers:
            Headers to add to the request.

        part:
            Whether the headers should be added to the request or the
            response.

            If `None`, then the function intelligently determines whether
            the headers should be added to the request or the response,
            based on whether the
            [`will_respond_with`][pact.interaction.HttpInteraction.will_respond_with]
            method has been called.
    """
    if isinstance(headers, dict):
        headers = headers.items()
    for name, value in headers:
        self.set_header(name, value, part)
    return self
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: int

Source code in src/pact/interaction/_http_interaction.py
def will_respond_with(self, 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`][pact.interaction.HttpInteraction.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.

    Args:
        status:
            Status for the response.
    """
    pact_ffi.response_status(self._handle, status)
    self.__interaction_part = pact_ffi.InteractionPart.RESPONSE
    return self
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

(
    pact.upon_receiving("a request")
    .with_header("X-Foo", "bar")
    .with_header("X-Foo", "baz")
)

will expect a request with the following headers:

X-Foo: bar
X-Foo: baz
# Or, equivalently:
X-Foo: bar, baz

Note that repeated headers are case insensitive in accordance with RFC 9110 §5.1.

PARAMETER DESCRIPTION
name

Name of the header.

TYPE: str

value

Value of the header.

TYPE: str | dict[str, str] | Matcher[object]

part

Whether the header should be added to the request or the response.

If None, then the function intelligently determines whether the header should be added to the request or the response, based on whether the will_respond_with method has been called.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/interaction/_http_interaction.py
def with_header(
    self,
    name: str,
    value: str | dict[str, str] | Matcher[object],
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    r"""
    Add a header to the request.

    If the same header has multiple values (see [RFC9110
    §5.2](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.2)), then
    the same header must be specified multiple times with _order being
    preserved_. For example

    ```python
    (
        pact.upon_receiving("a request")
        .with_header("X-Foo", "bar")
        .with_header("X-Foo", "baz")
    )
    ```

    will expect a request with the following headers:

    ```http
    X-Foo: bar
    X-Foo: baz
    # Or, equivalently:
    X-Foo: bar, baz
    ```

    Note that repeated headers are _case insensitive_ in accordance with
    [RFC 9110
    §5.1](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.1).

    Args:
        name:
            Name of the header.

        value:
            Value of the header.

        part:
            Whether the header should be added to the request or the
            response.

            If `None`, then the function intelligently determines whether
            the header should be added to the request or the response, based
            on whether the
            [`will_respond_with`][pact.interaction.HttpInteraction.will_respond_with]
            method has been called.
    """
    interaction_part = self._parse_interaction_part(part)
    name_lower = name.lower()
    index = self._request_indices[(interaction_part, name_lower)]
    self._request_indices[(interaction_part, name_lower)] += 1
    if not isinstance(value, str):
        value_str: str = json.dumps(value, cls=IntegrationJSONEncoder)
    else:
        value_str = value
    pact_ffi.with_header_v2(
        self._handle,
        interaction_part,
        name,
        index,
        value_str,
    )
    return self
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.

TYPE: dict[str, str] | Iterable[tuple[str, str]]

part

Whether the header should be added to the request or the response.

If None, then the function intelligently determines whether the header should be added to the request or the response, based on whether the will_respond_with method has been called.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/interaction/_http_interaction.py
def with_headers(
    self,
    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](https://pypi.org/project/multidict/).

    -   Passing in an iterable of key-value tuples.

    -   Make multiple calls to this function or
        [`with_header`][pact.interaction.HttpInteraction.with_header].

    See
    [`with_header`][pact.interaction.HttpInteraction.with_header]
    for more information.

    Args:
        headers:
            Headers to add to the request.

        part:
            Whether the header should be added to the request or the
            response.

            If `None`, then the function intelligently determines whether
            the header should be added to the request or the response, based
            on whether the
            [`will_respond_with`][pact.interaction.HttpInteraction.will_respond_with]
            method has been called.
    """
    if isinstance(headers, dict):
        headers = headers.items()
    for name, value in headers:
        self.with_header(name, value, part)
    return self
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: str

value

Value of the query parameter.

TYPE: object | Matcher[object]

Source code in src/pact/interaction/_http_interaction.py
def with_query_parameter(
    self,
    name: str,
    value: object | Matcher[object],
) -> Self:
    r"""
    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:

    ```python
    (
        pact.upon_receiving("a request")
        .with_query_parameter("name", "John")
        .with_query_parameter("name", "Mary")
    )
    ```

    Args:
        name:
            Name of the query parameter.

        value:
            Value of the query parameter.
    """
    index = self._parameter_indices[name]
    self._parameter_indices[name] += 1
    if not isinstance(value, str):
        value_str: str = json.dumps(value, cls=IntegrationJSONEncoder)
    else:
        value_str = value
    pact_ffi.with_query_parameter_v2(
        self._handle,
        name,
        index,
        value_str,
    )
    return self
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: Mapping[str, object | Matcher[object]] | Iterable[tuple[str, object | Matcher[object]]]

Source code in src/pact/interaction/_http_interaction.py
def with_query_parameters(
    self,
    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](https://pypi.org/project/multidict/).

    -   Passing in an iterable of key-value tuples.

    -   Make multiple calls to this function or
        [`with_query_parameter`][pact.interaction.HttpInteraction.with_query_parameter].

    See
    [`with_query_parameter`][pact.interaction.HttpInteraction.with_query_parameter]
    for more information.

    Args:
        parameters:
            Query parameters to add to the request.
    """
    if isinstance(parameters, Mapping):
        parameters = parameters.items()
    for name, value in parameters:
        self.with_query_parameter(name, value)
    return self
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: str

path

Path for the request.

TYPE: str | Matcher[object]

Source code in src/pact/interaction/_http_interaction.py
def with_request(self, method: str, path: str | Matcher[object]) -> Self:
    """
    Set the request.

    This is the request that the consumer will make to the provider.

    Args:
        method:
            HTTP method for the request.

        path:
            Path for the request.
    """
    if isinstance(path, Matcher):
        path_str = json.dumps(path, cls=IntegrationJSONEncoder)
    else:
        path_str = path
    pact_ffi.with_request(self._handle, method, path_str)
    return self

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: str

Source code in src/pact/interaction/_base.py
def __init__(self, description: str) -> None:
    """
    Create a new Interaction.

    As this class is abstract, this function should not be called directly
    but should instead be called through one of the concrete subclasses.

    Args:
        description:
            Description of the interaction. This must be unique within the
            Pact.
    """
    self._description = description

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: str

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
def add_text_comment(self, comment: str) -> Self:
    """
    Add a text comment for the interaction.

    This is used by V4 interactions to set arbitrary text comments for the
    interaction.

    Args:
        comment:
            Text of the comment.

    # 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`][pact.interaction.Interaction.set_comment].
    """
    pact_ffi.add_text_comment(self._handle, comment)
    return self
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:

pact.upon_receiving("a request").given("a user exists")

In many circumstances, it is useful to parameterize the state with additional data. In the example above, this could be with:

pact.upon_receiving("a request").given(
    "a user exists",
    id=123,
    name="Alice",
)

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: str

parameters

Should some of the parameters not be valid Python key identifiers, a dictionary can be passed in as the second positional argument.

pact.upon_receiving("A user request").given(
    "The given user exists",
    {"user-id": 123},
)

TYPE: dict[str, object] | None DEFAULT: None

kwargs

The additional parameters for the provider state, specified as additional arguments to the function. The values must be serializable using Python's json.dumps function.

TYPE: object DEFAULT: {}

Source code in src/pact/interaction/_base.py
def given(
    self,
    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:

    ```python
    pact.upon_receiving("a request").given("a user exists")
    ```

    In many circumstances, it is useful to parameterize the state with
    additional data. In the example above, this could be with:

    ```python
    pact.upon_receiving("a request").given(
        "a user exists",
        id=123,
        name="Alice",
    )
    ```

    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.

    Args:
        state:
            Provider state for the Interaction.

        parameters:
            Should some of the parameters not be valid Python key
            identifiers, a dictionary can be passed in as the second
            positional argument.

            ```python
            pact.upon_receiving("A user request").given(
                "The given user exists",
                {"user-id": 123},
            )
            ```

        kwargs:
            The additional parameters for the provider state, specified as
            additional arguments to the function. The values must be
            serializable using Python's [`json.dumps`][json.dumps]
            function.
    """
    if not parameters and not kwargs:
        pact_ffi.given(self._handle, state)
    else:
        pact_ffi.given_with_params(
            self._handle,
            state,
            json.dumps({**(parameters or {}), **kwargs}),
        )

    return self
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: str

value

Value for the comment. This must be encodable using json.dumps, or an existing JSON string. The value of None will remove the comment with the given key.

TYPE: Any | None

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
def set_comment(self, key: str, value: Any | None) -> Self:  # noqa: ANN401
    """
    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.

    Args:
        key:
            Key for the comment.

        value:
            Value for the comment. This must be encodable using
            [`json.dumps`][json.dumps], or an existing JSON string. The
            value of `None` will remove the comment with the given key.

    # Warning

    This function will overwrite any existing comment with the same key. In
    particular, the `text` key is used by `add_text_comment`.
    """
    if isinstance(value, str) or value is None:
        pact_ffi.set_comment(self._handle, key, value)
    else:
        pact_ffi.set_comment(self._handle, key, json.dumps(value))
    return self
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
def set_key(self, 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.
    """
    pact_ffi.set_key(self._handle, key)
    return self
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.

TYPE: dict[str, str] | None DEFAULT: None

part

Whether the metadata should be added to the request or the response. If None, then the function intelligently determines whether the body should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

**kwargs

Additional metadata key-value pairs.

TYPE: str DEFAULT: {}

RETURNS DESCRIPTION
Self

The current instance of the interaction.

Source code in src/pact/interaction/_base.py
def set_metadata(
    self,
    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`][pact.interaction.Interaction.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.

    Args:
        metadata:
            Dictionary of metadata keys and associated values.

        part:
            Whether the metadata should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the body should be added to the request or the response.

        **kwargs:
            Additional metadata key-value pairs.

    Returns:
        The current instance of the interaction.
    """
    interaction_part = self._parse_interaction_part(part)
    for k, v in (metadata or {}).items():
        pact_ffi.with_metadata(self._handle, k, v, interaction_part)
    for k, v in kwargs.items():
        pact_ffi.with_metadata(self._handle, k, v, interaction_part)
    return self
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
def set_pending(self, *, 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.
    """
    pact_ffi.set_pending(self._handle, pending=pending)
    return self
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: str

Source code in src/pact/interaction/_base.py
def test_name(
    self,
    name: str,
) -> Self:
    """
    Set the test name annotation for the interaction.

    This is used by V4 interactions to set the name of the test.

    Args:
        name:
            Name of the test.
    """
    pact_ffi.interaction_test_name(self._handle, name)
    return self
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: bytes | None

content_type

Content type of the body. This is ignored if the Content-Type header has already been set.

TYPE: str | None DEFAULT: None

part

Whether the body should be added to the request or the response. If None, then the function intelligently determines whether the body should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/interaction/_base.py
def with_binary_body(
    self,
    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`][pact.interaction.Interaction.with_body].

    Args:
        body:
            Body of the request.

        content_type:
            Content type of the body. This is ignored if the `Content-Type`
            header has already been set.

        part:
            Whether the body should be added to the request or the response.
            If `None`, then the function intelligently determines whether
            the body should be added to the request or the response.
    """
    pact_ffi.with_binary_body(
        self._handle,
        self._parse_interaction_part(part),
        content_type,
        body,
    )
    return self
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 None, then the body is empty.

TYPE: str | dict[str, Any] | Matcher[Any] | None DEFAULT: None

content_type

Content type of the body. This is ignored if the Content-Type header has already been set.

TYPE: str | None DEFAULT: None

part

Whether the body should be added to the request or the response. If None, then the function intelligently determines whether the body should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/interaction/_base.py
def with_body(
    self,
    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.

    Args:
        body:
            Body of the request. If this is `None`, then the body is
            empty.

        content_type:
            Content type of the body. This is ignored if the `Content-Type`
            header has already been set.

        part:
            Whether the body should be added to the request or the response.
            If `None`, then the function intelligently determines whether
            the body should be added to the request or the response.
    """
    if body and isinstance(body, str):
        body_str = body
    else:
        body_str = json.dumps(body, cls=IntegrationJSONEncoder)

    pact_ffi.with_body(
        self._handle,
        self._parse_interaction_part(part),
        content_type,
        body_str,
    )
    return self
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 json.dumps, or a string.

TYPE: dict[str, Any] | str

part

Whether the generators should be added to the request or the response. If None, then the function intelligently determines whether the generators should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/interaction/_base.py
def with_generators(
    self,
    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.

    Args:
        generators:
            Generators to add to the interaction. This must be encodable
            using [`json.dumps`][json.dumps], or a string.

        part:
            Whether the generators should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the generators should be added to the request or the
            response.
    """
    if isinstance(generators, dict):
        generators = json.dumps(generators)

    pact_ffi.with_generators(
        self._handle,
        self._parse_interaction_part(part),
        generators,
    )
    return self
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 json.dumps, or a string.

TYPE: dict[str, Any] | str

part

Whether the matching rules should be added to the request or the response. If None, then the function intelligently determines whether the matching rules should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/interaction/_base.py
def with_matching_rules(
    self,
    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.

    Args:
        rules:
            Matching rules to add to the interaction. This must be encodable
            using [`json.dumps`][json.dumps], or a string.

        part:
            Whether the matching rules should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the matching rules should be added to the request or the
            response.
    """
    if isinstance(rules, dict):
        rules = json.dumps(rules)

    pact_ffi.with_matching_rules(
        self._handle,
        self._parse_interaction_part(part),
        rules,
    )
    return self
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: dict[str, object | Matcher[object]] | None DEFAULT: None

part

Whether the metadata should be added to the request or the response. If None, then the function intelligently determines whether the body should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

**kwargs

Additional metadata key-value pairs.

TYPE: object | Matcher[object] DEFAULT: {}

RETURNS DESCRIPTION
Self

The current instance of the interaction.

Source code in src/pact/interaction/_base.py
def with_metadata(
    self,
    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):

    ```python
    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`][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`][pact.interaction.Interaction.set_metadata] method as
    this does not perform any additional parsing of the string.

    Args:
        metadata:
            Dictionary of metadata keys and associated values.

        part:
            Whether the metadata should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the body should be added to the request or the response.

        **kwargs:
            Additional metadata key-value pairs.

    Returns:
        The current instance of the interaction.
    """
    interaction_part = self._parse_interaction_part(part)
    for k, v in (metadata or {}).items():
        pact_ffi.with_metadata(
            self._handle,
            k,
            json.dumps(v, cls=IntegrationJSONEncoder),
            interaction_part,
        )
    for k, v in kwargs.items():
        pact_ffi.with_metadata(
            self._handle,
            k,
            json.dumps(v, cls=IntegrationJSONEncoder),
            interaction_part,
        )
    return self
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: str

path

Path to the file to add.

TYPE: Path | None

content_type

Content type of the part.

TYPE: str | None DEFAULT: None

part

Whether the part should be added to the request or the response. If None, then the function intelligently determines whether the part should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

boundary

Boundary string for the multipart message.

TYPE: str | None DEFAULT: None

Source code in src/pact/interaction/_base.py
def with_multipart_file(
    self,
    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.

    Args:
        part_name:
            Name of the multipart part.

        path:
            Path to the file to add.

        content_type:
            Content type of the part.

        part:
            Whether the part should be added to the request or the
            response.
            If `None`, then the function intelligently determines whether
            the part should be added to the request or the response.

        boundary:
            Boundary string for the multipart message.
    """
    pact_ffi.with_multipart_file_v2(
        self._handle,
        self._parse_interaction_part(part),
        content_type,
        path,
        part_name,
        boundary,
    )
    return self
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 None, then the body is empty.

TYPE: dict[str, Any] | str

content_type

Content type of the body. This is ignored if the Content-Type header has already been set.

TYPE: str

part

Whether the body should be added to the request or the response. If None, then the function intelligently determines whether the body should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/interaction/_base.py
def with_plugin_contents(
    self,
    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.

    Args:
        contents:
            Body of the request. If this is `None`, then the body is empty.

        content_type:
            Content type of the body. This is ignored if the `Content-Type`
            header has already been set.

        part:
            Whether the body should be added to the request or the response.
            If `None`, then the function intelligently determines whether
            the body should be added to the request or the response.
    """
    if isinstance(contents, dict):
        contents = json.dumps(contents)

    pact_ffi.interaction_contents(
        self._handle,
        self._parse_interaction_part(part),
        content_type,
        contents,
    )
    return self

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: PactHandle

description

Description of the interaction. This must be unique within the Pact.

TYPE: str

Source code in src/pact/interaction/_sync_message_interaction.py
def __init__(self, pact_handle: pact_ffi.PactHandle, description: str) -> None:
    """
    Initialise a new Synchronous Message Interaction.

    This function should not be called directly. Instead, an
    AsyncMessageInteraction should be created using the
    [`upon_receiving`][pact.Pact.upon_receiving] method of a
    [`Pact`][pact.Pact] instance using the `"Sync"` interaction type.

    Args:
        pact_handle:
            Handle for the Pact.

        description:
            Description of the interaction. This must be unique within the
            Pact.
    """
    super().__init__(description)
    self.__handle = pact_ffi.new_sync_message_interaction(
        pact_handle,
        description,
    )
    self.__interaction_part = pact_ffi.InteractionPart.REQUEST

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.

Source code in src/pact/interaction/_sync_message_interaction.py
def will_respond_with(self) -> 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()`][pact.pact.HttpInteraction.will_respond_with]
    method of the [`HttpInteraction`][pact.pact.HttpInteraction] class,
    albeit more generic for synchronous message interactions.

    For example, the following two snippets are equivalent:

    ```python
    Pact(...).upon_receiving("A sync request", interaction="Sync")
        .with_body("request body", part="Request")
        .with_body("response body", part="Response")
    ```

    ```python
    Pact(...).upon_receiving("A sync request", interaction="Sync")
        .with_body("request body")
        .will_respond_with()
        .with_body("response body")
    ```

    Returns:
        The current instance of the interaction.
    """
    self.__interaction_part = pact_ffi.InteractionPart.RESPONSE
    return self