Repository: pact-foundation/pact-python (language: python)

Issue:
Examples: remove misleading use of PACT_MOCK variables in examples
The fastapi example (and perhaps the others) are a little misleading to newbies.

The following [lines](https://github.com/pact-foundation/pact-python/blob/master/examples/fastapi_e2e/tests/provider/test_provider.py#L23-L25) would suggest that there is pact mock on the provider side, when in reality this is the address of the locally running FastAPI provider:

```python
PACT_MOCK_HOST = 'localhost'
PACT_MOCK_PORT = 8000
PACT_URL = "http://{}:{}".format(PACT_MOCK_HOST, PACT_MOCK_PORT)
```

I'd suggest changing these to `PROVIDER_HOST`, `PROVIDER_PORT` and `PROVIDER_URL` respectively, perhaps with a comment explaining these map to the default host/port combination of the provider.

See this thread for context: https://pact-foundation.slack.com/archives/C9VECUP6E/p1633686950075000


Discussion / hints:

Fix patch:
diff --git a/.flake8 b/.flake8
index a9c848e34b..d51598ba5d 100644
--- a/.flake8
+++ b/.flake8
@@ -2,3 +2,4 @@
 ignore = E226,E302,E41,W503
 max-line-length = 160
 max-complexity = 10
+exclude = .git,venv,.tox
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 1e82f5f0d3..a1e4a2528a 100644
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,12 @@ help:
 	@echo ""
 	@echo "  clean      to clear build and distribution directories"
 	@echo "  deps       to install the required files for development"
-	@echo "  e2e        to run the end to end tests"
 	@echo "  verifier   to run the verifier end to end tests"
-	@echo "  examples   to run the example end to end tests"
+	@echo "  examples   to run the example end to end tests (consumer, fastapi, flask, messaging)"
+	@echo "  consumer   to run the example consumer tests"
+	@echo "  fastapi    to run the example FastApi provider tests"
+	@echo "  flask      to run the example Flask provider tests"
+	@echo "  messaging  to run the example messaging e2e tests"
 	@echo "  package    to create a distribution package in /dist/"
 	@echo "  release    to perform a release build, including deps, test, and package targets"
 	@echo "  test       to run all tests"
@@ -30,38 +33,69 @@ deps:
 	pip install -r requirements_dev.txt -e .
 
 
-define E2E
-	echo "e2e make"
-	cd examples/e2e
-  pip install -r requirements.txt
-  pip install -e ../../
-  ./run_pytest.sh 
+define CONSUMER
+	echo "consumer make"
+	cd examples/consumer
+	pip install -q -r requirements.txt
+	pip install -e ../../
+	./run_pytest.sh
 endef
-export E2E
+export CONSUMER
 
 
-define messaging
+define FLASK_PROVIDER
+	echo "flask make"
+	cd examples/flask_provider
+	pip install -q -r requirements.txt
+	pip install -e ../../
+	./run_pytest.sh
+endef
+export FLASK_PROVIDER
+
+
+define FASTAPI_PROVIDER
+	echo "fastapi make"
+	cd examples/fastapi_provider
+	pip install -q -r requirements.txt
+	pip install -e ../../
+	./run_pytest.sh
+endef
+export FASTAPI_PROVIDER
+
+
+define MESSAGING
 	echo "messaging make"
 	cd examples/message
-  pip install -r requirements.txt
-  pip install -e ../../
-  ./run_pytest.sh
+	pip install -q -r requirements.txt
+	pip install -e ../../
+	./run_pytest.sh
 endef
-export messaging
+export MESSAGING
+
+
+.PHONY: consumer
+consumer:
+	bash -c "$$CONSUMER"
+
+
+.PHONY: flask
+flask:
+	bash -c "$$FLASK_PROVIDER"
 
 
-.PHONY: e2e
-e2e:
-	bash -c "$$E2E"
+.PHONY: fastapi
+fastapi:
+	bash -c "$$FASTAPI_PROVIDER"
 
 
 .PHONY: messaging
 messaging:
-	bash -c "$$messaging"
+	bash -c "$$MESSAGING"
 
 
 .PHONY: examples
-examples: e2e messaging
+examples: consumer flask messaging
+# TODO: Fix fastapi, to run all examples this should be: consumer flask fastapi messaging
 
 
 .PHONY: package
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000000..6b1672e1ec
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,156 @@
+# Examples
+
+## Table of Contents
+
+  * [Overview](#overview)
+  * [broker](#broker)
+  * [consumer](#consumer)
+  * [flask_provider](#flask_provider)
+  * [fastapi_provider](#fastapi_provider)
+  * [message](#message)
+  * [pacts](#pacts)
+
+## Overview
+
+Here you can find examples of how to use Pact using the python language. You can find more of an overview on Pact in the
+[Pact Introduction].
+
+Examples are given of both the [Consumer] and [Provider], this does not mean however that you must use python for both.
+Different languages can be mixed and matched as required.
+
+In these examples, `1` is just used to meet the need of having *some* [Consumer] or [Provider] version. In reality, you
+will generally want to use something more complicated and automated. Guidelines and best practices are available in the
+[Versioning in the Pact Broker]
+
+## broker
+
+The [Pact Broker] stores [Pact file]s and [Pact verification] results. It is used here for the [consumer](#consumer),
+[flask_provider](#flask-provider) and [message](#message) tests.
+
+### Running
+
+These examples run the [Pact Broker] as part of the tests when specified. It can be run outside the tests as well by
+performing the following command from a separate terminal in the `examples/broker` folder:
+```bash
+docker-compose up
+```
+
+You should then be able to open a browser and navigate to http://localhost where you will initially be able to see the
+default Example App/Example API Pact.
+
+Running the [Pact Broker] outside the tests will mean you are able to then see the [Pact file]s submitted to the
+[Pact Broker] as the various tests are performed.
+
+## consumer
+
+Pact is consumer-driven, which means first the contracts are created. These Pact contracts are generated during
+execution of the consumer tests.
+
+### Running
+
+When the tests are run, the "minimum" is to generate the Pact contract JSON, additional options are available. The
+following commands can be run from the `examples/consumer` folder:
+
+- To startup the broker, run the tests, and publish the results to the broker:
+    ```bash
+    pytest --run-broker True --publish-pact 1 
+    ```
+- Alternatively the same can be performed with the following command, which is called from a `make consumer`:
+    ```bash
+    ./run_pytest.sh
+    ```
+- To run the tests, and publish the results to the broker which is already running:
+    ```bash
+    pytest --publish-pact 1
+    ```
+- To just run the tests:
+    ```bash
+    pytest
+    ```
+
+### Output
+
+The following file(s) will be created when the tests are run:
+
+| Filename                                    | Contents  |
+|---------------------------------------------| ----------|
+| consumer/pact-mock-service.log              | All interactions with the mock provider such as expected interactions, requests, and interaction verifications. |
+| consumer/userserviceclient-userservice.json | This contains the Pact interactions between the `UserServiceClient` and `UserService`, as defined in the tests. The naming being derived from the named Pacticipants: `Consumer("UserServiceClient")` and `Provider("UserService")` |
+
+## flask_provider
+
+The Flask [Provider] example consists of a basic Flask app, with a single endpoint route.
+This implements the service expected by the [consumer](#consumer).
+
+The [Provider] side is responsible for performing the tests to verify if it is compliant with the [Pact file] contracts
+associated with it.
+
+As such, the tests use the pact-python Verifier to perform this verification. Two approaches are demonstrated:
+- Testing against the [Pact broker]. Generally this is the preferred approach, see information on [Sharing Pacts].
+- Testing against the [Pact file] directly. If no [Pact broker] is available you can verify against a static [Pact file].
+
+### Running
+
+To avoid package version conflicts with different applications, it is recommended to run these tests from a
+[Virtual Environment]
+
+The following commands can be run from within your [Virtual Environment], in the `examples/flask_provider`.
+
+To perform the python tests:
+```bash
+pip install -r requirements.txt # Install the dependencies for the Flask example
+pip install -e ../../           # Using setup.py in the pact-python root, install any pact dependencies and pact-python
+./run_pytest.sh                 # Wrapper script to first run Flask, and then run the tests
+```
+
+To perform verification using CLI to verify the [Pact file] against the Flask [Provider] instead of the python tests:
+```bash
+pip install -r requirements.txt # Install the dependencies for the Flask example
+./verify_pact.sh                # Wrapper script to first run Flask, and then use `pact-verifier` to verify locally
+```
+
+To perform verification using CLI, but verifying the [Pact file] previously provided by a [Consumer], and publish the
+results. This example requires that the [Pact broker] is already running, and the [Consumer] tests have been published
+already, described in the [consumer](#consumer) section above.
+```bash
+pip install -r requirements.txt # Install the dependencies for the Flask example
+./verify_pact.sh 1              # Wrapper script to first run Flask, and then use `pact-verifier` to verify and publish
+```
+
+These examples demonstrate by first launching Flask via a `python -m flask run`, you may prefer to start Flask using an
+`app.run()` call in the python code instead, see [How to Run a Flask Application]. Additionally for tests, you may want
+to manage starting and stopping Flask as part of a fixture setup. Any approach can be chosen here, in line with your
+existing Flask testing practices.
+
+### Output
+
+The following file(s) will be created when the tests are run
+
+| Filename                    | Contents  |
+|-----------------------------| ----------|
+| flask_provider/log/pact.log | All Pact interactions with the Flask Provider. Every interaction example retrieved from the Pact Broker will be performed during the Verification test; the request/response logged here. | 
+
+## fastapi_provider
+
+TODO
+
+## message
+
+TODO
+
+## pacts
+
+Both the Flask and the FastAPI [Provider] examples implement the same service the [Consumer] example interacts with.
+This folder contains the generated [Pact file] for reference, which is also used when running the [Provider] tests
+without a [Pact Broker].
+
+[Pact Broker]: https://docs.pact.io/pact_broker
+[Pact Introduction]: https://docs.pact.io/
+[Consumer]: https://docs.pact.io/getting_started/terminology#service-consumer
+[Provider]: https://docs.pact.io/getting_started/terminology#service-provider
+[Versioning in the Pact Broker]: https://docs.pact.io/getting_started/versioning_in_the_pact_broker/
+[Pact file]: https://docs.pact.io/getting_started/terminology#pact-file
+[Pact verification]: https://docs.pact.io/getting_started/terminology#pact-verification]
+[Virtual Environment]: https://docs.python.org/3/tutorial/venv.html
+[Sharing Pacts]: https://docs.pact.io/getting_started/sharing_pacts/]
+[How to Run a Flask Application]: https://www.twilio.com/blog/how-run-flask-application
\ No newline at end of file
diff --git a/examples/broker/docker-compose.yml b/examples/broker/docker-compose.yml
index af8bd2479c..801e608865 100644
--- a/examples/broker/docker-compose.yml
+++ b/examples/broker/docker-compose.yml
@@ -1,7 +1,7 @@
 version: '3'
 
 services:
-
+  # A PostgreSQL database for the Broker to store Pacts and verification results
   postgres:
     image: postgres
     healthcheck:
@@ -13,10 +13,16 @@ services:
       POSTGRES_PASSWORD: password
       POSTGRES_DB: postgres
 
+  # The Pact Broker
   broker_app:
-    image: dius/pact-broker
+    # Alternatively the DiUS Pact Broker can be used:
+    # image: dius/pact-broker
+    #
+    # As well as changing the image, the destination port will need to be changed
+    # from 9292 below, and in the nginx.conf proxy_pass section
+    image: pactfoundation/pact-broker
     ports:
-      - "80:80"
+      - "80:9292"
     links:
       - postgres
     environment:
@@ -27,6 +33,8 @@ services:
       PACT_BROKER_BASIC_AUTH_USERNAME: pactbroker
       PACT_BROKER_BASIC_AUTH_PASSWORD: pactbroker
 
+  # An NGINX reverse proxy in front of the Broker on port 8443, to be able to
+  # terminate with SSL
   nginx:
     image: nginx:alpine
     links:
diff --git a/examples/broker/ssl/nginx.conf b/examples/broker/ssl/nginx.conf
index 83eea6cde1..e3c6bb36ef 100644
--- a/examples/broker/ssl/nginx.conf
+++ b/examples/broker/ssl/nginx.conf
@@ -1,21 +1,23 @@
 server {
-  listen      443 ssl default_server;
-  server_name localhost;
-  ssl_certificate /etc/nginx/ssl/nginx-selfsigned.crt;
-  ssl_certificate_key /etc/nginx/ssl/nginx-selfsigned.key;
-  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
-  ssl_prefer_server_ciphers on;
-  ssl_ecdh_curve secp384r1;
-  ssl_session_cache shared:SSL:10m;
-  ssl_stapling on;
-  ssl_stapling_verify on;
+    listen 443 ssl default_server;
+    server_name localhost;
+    ssl_certificate /etc/nginx/ssl/nginx-selfsigned.crt;
+    ssl_certificate_key /etc/nginx/ssl/nginx-selfsigned.key;
+    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+    ssl_prefer_server_ciphers on;
+    ssl_ecdh_curve secp384r1;
+    ssl_session_cache shared:SSL:10m;
+    ssl_stapling on;
+    ssl_stapling_verify on;
 
-  location / {
-      proxy_pass http://broker:80;
-      proxy_set_header Host $host;
-      proxy_set_header X-Forwarded-Scheme "https";
-      proxy_set_header X-Forwarded-Port "443";
-      proxy_set_header X-Forwarded-Ssl "on";
-      proxy_set_header X-Real-IP $remote_addr;
-  }
+    location / {
+        # To use with the Dius Pact Broker:
+        # proxy_pass http://broker:80;
+        proxy_pass http://broker:9292;
+        proxy_set_header Host $host;
+        proxy_set_header X-Forwarded-Scheme "https";
+        proxy_set_header X-Forwarded-Port "443";
+        proxy_set_header X-Forwarded-Ssl "on";
+        proxy_set_header X-Real-IP $remote_addr;
+    }
 }
diff --git a/examples/consumer/requirements.txt b/examples/consumer/requirements.txt
new file mode 100644
index 0000000000..eb82e9fb0e
--- /dev/null
+++ b/examples/consumer/requirements.txt
@@ -0,0 +1,3 @@
+pytest==5.4.1
+requests>=2.26.0
+testcontainers==3.3.0
\ No newline at end of file
diff --git a/examples/consumer/src/consumer.py b/examples/consumer/src/consumer.py
new file mode 100644
index 0000000000..6d6ed32688
--- /dev/null
+++ b/examples/consumer/src/consumer.py
@@ -0,0 +1,40 @@
+from typing import Optional
+
+import requests
+from datetime import datetime
+
+
+class User(object):
+    """Define the basic User data we expect to receive from the User Provider."""
+
+    def __init__(self, name: str, created_on: str):
+        self.name = name
+        self.created_on = created_on
+
+
+class UserConsumer(object):
+    """Demonstrate some basic functionality of how the User Consumer will interact
+    with the User Provider, in this case a simple get_user."""
+
+    def __init__(self, base_uri: str):
+        """Initialise the Consumer, in this case we only need to know the URI.
+
+        :param base_uri: The full URI, including port of the Provider to connect to
+        """
+        self.base_uri = base_uri
+
+    def get_user(self, user_name: str) -> Optional[User]:
+        """Fetch a user object by user_name from the server.
+
+        :param user_name: User name to search for
+        :return: User details if found, None if not found
+        """
+        uri = self.base_uri + "/users/" + user_name
+        response = requests.get(uri)
+        if response.status_code == 404:
+            return None
+
+        name = response.json()["name"]
+        created_on = datetime.strptime(response.json()["created_on"], "%Y-%m-%dT%H:%M:%S")
+
+        return User(name, created_on)
diff --git a/examples/e2e/.flake8 b/examples/fastapi_provider/.flake8
similarity index 100%
rename from examples/e2e/.flake8
rename to examples/fastapi_provider/.flake8
diff --git a/examples/fastapi_e2e/pythonclient-pythonservice.json b/examples/fastapi_provider/pythonclient-pythonservice.json
similarity index 100%
rename from examples/fastapi_e2e/pythonclient-pythonservice.json
rename to examples/fastapi_provider/pythonclient-pythonservice.json
diff --git a/examples/fastapi_provider/requirements.txt b/examples/fastapi_provider/requirements.txt
new file mode 100644
index 0000000000..b99ec5a210
--- /dev/null
+++ b/examples/fastapi_provider/requirements.txt
@@ -0,0 +1,4 @@
+fastapi==0.67.0
+pytest==5.4.1
+requests>=2.26.0
+uvicorn>=0.14.0
diff --git a/examples/fastapi_e2e/src/provider.py b/examples/fastapi_provider/src/provider.py
similarity index 54%
rename from examples/fastapi_e2e/src/provider.py
rename to examples/fastapi_provider/src/provider.py
index 9ea3e5ce0a..9703c0b14e 100644
--- a/examples/fastapi_e2e/src/provider.py
+++ b/examples/fastapi_provider/src/provider.py
@@ -1,18 +1,11 @@
-from fastapi import FastAPI, APIRouter, HTTPException
-from pydantic import BaseModel
-
+from fastapi import FastAPI, HTTPException, APIRouter
 
+fakedb = {}  # Use a simple dict to represent a database
 router = APIRouter()
-
-fakedb = {}
 app = FastAPI()
 
 
-class TestUser(BaseModel):
-    name: str  # noqa: E999
-
-
-@router.get('/users/{name}')
+@app.get("/users/{name}")
 def get_user_by_name(name: str):
     user_data = fakedb.get(name)
     if not user_data:
diff --git a/examples/fastapi_e2e/verify_pact.sh b/examples/fastapi_provider/verify_pact.sh
similarity index 100%
rename from examples/fastapi_e2e/verify_pact.sh
rename to examples/fastapi_provider/verify_pact.sh
diff --git a/examples/fastapi_e2e/.flake8 b/examples/flask_provider/.flake8
similarity index 100%
rename from examples/fastapi_e2e/.flake8
rename to examples/flask_provider/.flake8
diff --git a/examples/e2e/requirements.txt b/examples/flask_provider/requirements.txt
similarity index 73%
rename from examples/e2e/requirements.txt
rename to examples/flask_provider/requirements.txt
index a11e78ea9f..e980564d0e 100644
--- a/examples/e2e/requirements.txt
+++ b/examples/flask_provider/requirements.txt
@@ -1,4 +1,4 @@
 Flask==1.1.1
 pytest==5.4.1
-requests==2.23.0
+requests>=2.26.0
 testcontainers==3.3.0
\ No newline at end of file
diff --git a/examples/flask_provider/src/provider.py b/examples/flask_provider/src/provider.py
new file mode 100644
index 0000000000..3b21a7f36e
--- /dev/null
+++ b/examples/flask_provider/src/provider.py
@@ -0,0 +1,25 @@
+from flask import Flask, abort, jsonify
+
+fakedb = {}  # Use a simple dict to represent a database
+
+app = Flask(__name__)
+
+
+@app.route("/users/<name>")
+def get_user_by_name(name: str):
+    """Handle requests to retrieve a single user from the simulated database.
+
+    :param name: Name of the user to "search for"
+    :return: The user data if found, None if not
+    """
+    user_data = fakedb.get(name)
+    if not user_data:
+        app.logger.debug(f"GET user for: '{name}', HTTP 404 not found")
+        abort(404)
+    response = jsonify(**user_data)
+    app.logger.debug(f"GET user for: '{name}', returning: {response.data}")
+    return response
+
+
+if __name__ == "__main__":
+    app.run(debug=True, port=5001)
diff --git a/examples/flask_provider/verify_pact.sh b/examples/flask_provider/verify_pact.sh
new file mode 100755
index 0000000000..f70629699e
--- /dev/null
+++ b/examples/flask_provider/verify_pact.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+set -o pipefail
+
+# Run the Flask server, using the pact_provider.py as the app to be able to
+# inject the provider_states endpoint
+FLASK_APP=tests/pact_provider.py python -m flask run -p 5001 &
+FLASK_PID=$!
+
+# Make sure the Flask server is stopped when finished to avoid blocking the port
+function teardown {
+  echo "Tearing down Flask server: ${FLASK_PID}"
+  kill -9 $FLASK_PID
+}
+trap teardown EXIT
+
+# Wait a little in case Flask isn't quite ready
+sleep 1
+
+VERSION=$1
+if [ -z "$VERSION" ];
+then
+  echo "Validating provider locally"
+
+  pact-verifier \
+    --provider-base-url=http://localhost:5001 \
+    --provider-states-setup-url=http://localhost:5001/_pact/provider_states \
+    ../pacts/userserviceclient-userservice.json
+else
+  echo "Validating against Pact Broker"
+
+  pact-verifier \
+    --provider-base-url=http://localhost:5001 \
+    --provider-app-version $VERSION \
+    --pact-url="http://127.0.0.1/pacts/provider/UserService/consumer/UserServiceClient/latest" \
+    --pact-broker-username pactbroker \
+    --pact-broker-password pactbroker \
+    --publish-verification-results \
+    --provider-states-setup-url=http://localhost:5001/_pact/provider_states
+fi
diff --git a/examples/message/requirements.txt b/examples/message/requirements.txt
index 93186fa97f..a77d279364 100644
--- a/examples/message/requirements.txt
+++ b/examples/message/requirements.txt
@@ -1,3 +1,3 @@
 Flask==1.1.1
 pytest==5.4.1
-requests==2.23.0
\ No newline at end of file
+requests>=2.26.0
\ No newline at end of file
diff --git a/examples/message/src/__init__.py b/examples/message/src/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/fastapi_e2e/tests/consumer/userserviceclient-userservice.json b/examples/pacts/userserviceclient-userservice.json
similarity index 100%
rename from examples/fastapi_e2e/tests/consumer/userserviceclient-userservice.json
rename to examples/pacts/userserviceclient-userservice.json
diff --git a/requirements_dev.txt b/requirements_dev.txt
index 7fd673cbcd..779907d412 100644
--- a/requirements_dev.txt
+++ b/requirements_dev.txt
@@ -10,7 +10,7 @@ pydocstyle==4.0.1
 tox==3.14.0
 pytest==5.4.1
 pytest-cov==2.11.1
-requests==2.26
+requests>=2.26.0
 tox-travis==0.8
 urllib3>=1.26.5
 wheel==0.24.0
diff --git a/setup.py b/setup.py
index 4a76af7f24..c3d8d07a1b 100644
--- a/setup.py
+++ b/setup.py
@@ -201,7 +201,7 @@ def read(filename):
 dependencies = [
     'click>=2.0.0',
     'psutil>=2.0.0',
-    'requests>=2.5.0',
+    'requests>=2.26.0',
     'six>=1.9.0',
     'fastapi>=0.67.0',
     'urllib3>=1.26.5',


Test patch:
diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
index 436b4bc028..a51e11b12b 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -29,4 +29,4 @@ jobs:
         tox -e test
     - name: Test examples
       run: |
-        make e2e
+        make examples
diff --git a/examples/consumer/conftest.py b/examples/consumer/conftest.py
new file mode 100644
index 0000000000..252c027ca9
--- /dev/null
+++ b/examples/consumer/conftest.py
@@ -0,0 +1,49 @@
+import pytest
+from testcontainers.compose import DockerCompose
+
+
+def pytest_addoption(parser):
+    parser.addoption(
+        "--publish-pact",
+        type=str,
+        action="store",
+        help="Upload generated Pact file to Pact Broker with the version provided",
+    )
+
+    parser.addoption("--run-broker", type=bool, action="store", help="Whether to run broker in this test or not")
+
+
+# This fixture is to simulate a managed Pact Broker or Pactflow account.
+# For almost all purposes outside this example, you will want to use a real
+# broker. See https://github.com/pact-foundation/pact_broker for further details.
+@pytest.fixture(scope="session", autouse=True)
+def broker(request):
+    version = request.config.getoption("--publish-pact")
+    publish = True if version else False
+
+    # If the results are not going to be published to the broker, there is
+    # nothing further to do anyway
+    if not publish:
+        yield
+        return
+
+    run_broker = request.config.getoption("--run-broker")
+
+    if run_broker:
+        # Start up the broker using docker-compose
+        print("Starting broker")
+        with DockerCompose("../broker", compose_file_name=["docker-compose.yml"], pull=True) as compose:
+            stdout, stderr = compose.get_logs()
+            if stderr:
+                print("Errors\\n:{}".format(stderr))
+            print("{}".format(stdout))
+            print("Started broker")
+
+            yield
+            print("Stopping broker")
+        print("Broker stopped")
+    else:
+        # Assuming there is a broker available already, docker-compose has been
+        # used manually as the --run-broker option has not been provided
+        yield
+        return
diff --git a/examples/consumer/run_pytest.sh b/examples/consumer/run_pytest.sh
new file mode 100755
index 0000000000..7494398bbf
--- /dev/null
+++ b/examples/consumer/run_pytest.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -o pipefail
+
+pytest tests --run-broker True --publish-pact 1
\ No newline at end of file
diff --git a/examples/consumer/tests/consumer/test_user_consumer.py b/examples/consumer/tests/consumer/test_user_consumer.py
new file mode 100644
index 0000000000..55f5fd1b4d
--- /dev/null
+++ b/examples/consumer/tests/consumer/test_user_consumer.py
@@ -0,0 +1,129 @@
+"""pact test for user service client"""
+
+import atexit
+import logging
+import os
+
+import pytest
+
+from pact import Consumer, Like, Provider, Term, Format
+from src.consumer import UserConsumer
+
+log = logging.getLogger(__name__)
+logging.basicConfig(level=logging.INFO)
+
+# If publishing the Pact(s), they will be submitted to the Pact Broker here.
+# For the purposes of this example, the broker is started up as a fixture defined
+# in conftest.py. For normal usage this would be self-hosted or using Pactflow.
+PACT_BROKER_URL = "http://localhost"
+PACT_BROKER_USERNAME = "pactbroker"
+PACT_BROKER_PASSWORD = "pactbroker"
+
+# Define where to run the mock server, for the consumer to connect to. These
+# are the defaults so may be omitted
+PACT_MOCK_HOST = "localhost"
+PACT_MOCK_PORT = 1234
+
+# Where to output the JSON Pact files created by any tests
+PACT_DIR = os.path.dirname(os.path.realpath(__file__))
+
+
+@pytest.fixture
+def consumer() -> UserConsumer:
+    return UserConsumer("http://{host}:{port}".format(host=PACT_MOCK_HOST, port=PACT_MOCK_PORT))
+
+
+@pytest.fixture(scope="session")
+def pact(request):
+    """Setup a Pact Consumer, which provides the Provider mock service. This
+    will generate and optionally publish Pacts to the Pact Broker"""
+
+    # When publishing a Pact to the Pact Broker, a version number of the Consumer
+    # is required, to be able to construct the compatability matrix between the
+    # Consumer versions and Provider versions
+    version = request.config.getoption("--publish-pact")
+    publish = True if version else False
+
+    pact = Consumer("UserServiceClient", version=version).has_pact_with(
+        Provider("UserService"),
+        host_name=PACT_MOCK_HOST,
+        port=PACT_MOCK_PORT,
+        pact_dir=PACT_DIR,
+        publish_to_broker=publish,
+        broker_base_url=PACT_BROKER_URL,
+        broker_username=PACT_BROKER_USERNAME,
+        broker_password=PACT_BROKER_PASSWORD,
+    )
+
+    pact.start_service()
+
+    # Make sure the Pact mocked provider is stopped when we finish, otherwise
+    # port 1234 may become blocked
+    atexit.register(pact.stop_service)
+
+    yield pact
+
+    # This will stop the Pact mock server, and if publish is True, submit Pacts
+    # to the Pact Broker
+    pact.stop_service()
+
+    # Given we have cleanly stopped the service, we do not want to re-submit the
+    # Pacts to the Pact Broker again atexit, since the Broker may no longer be
+    # available if it has been started using the --run-broker option, as it will
+    # have been torn down at that point
+    pact.publish_to_broker = False
+
+
+def test_get_user_non_admin(pact, consumer):
+    # Define the Matcher; the expected structure and content of the response
+    expected = {
+        "name": "UserA",
+        "id": Format().uuid,
+        "created_on": Term(r"\d+-\d+-\d+T\d+:\d+:\d+", "2016-12-15T20:16:01"),
+        "ip_address": Format().ip_address,
+        "admin": False,
+    }
+
+    # Define the expected behaviour of the Provider. This determines how the
+    # Pact mock provider will behave. In this case, we expect a body which is
+    # "Like" the structure defined above. This means the mock provider will
+    # return the EXACT content where defined, e.g. UserA for name, and SOME
+    # appropriate content e.g. for ip_address.
+    (
+        pact.given("UserA exists and is not an administrator")
+        .upon_receiving("a request for UserA")
+        .with_request("get", "/users/UserA")
+        .will_respond_with(200, body=Like(expected))
+    )
+
+    with pact:
+        # Perform the actual request
+        user = consumer.get_user("UserA")
+
+        # In this case the mock Provider will have returned a valid response
+        assert user.name == "UserA"
+
+        # Make sure that all interactions defined occurred
+        pact.verify()
+
+
+def test_get_non_existing_user(pact, consumer):
+    # Define the expected behaviour of the Provider. This determines how the
+    # Pact mock provider will behave. In this case, we expect a 404
+    (
+        pact.given("UserA does not exist")
+        .upon_receiving("a request for UserA")
+        .with_request("get", "/users/UserA")
+        .will_respond_with(404)
+    )
+
+    with pact:
+        # Perform the actual request
+        user = consumer.get_user("UserA")
+
+        # In this case, the mock Provider will have returned a 404 so the
+        # consumer will have returned None
+        assert user is None
+
+        # Make sure that all interactions defined occurred
+        pact.verify()
diff --git a/examples/e2e/README.md b/examples/e2e/README.md
deleted file mode 100644
index 0b6d50925c..0000000000
--- a/examples/e2e/README.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Introduction
-
-This is an e2e example to show the provider verification working for both cli and python api. 
-
-Note in this example we are within conftest we are creating a Broker instance. This is purely to help demonstrate using Pact Broker in the code and you are strongly advised to set up a persistent Broker or use Pactflow
-
-## Setup
-
-Create your own virtualenv for this. Run
-
-```bash
-pip install -r requirements.txt
-pip install -e ../../
-./run_pytest.sh
-```
-
-This should provide you with a relative path to pact install relatively (2 dirs up) You can look at the Makefile in the root folder for more details.
-
-
-# Running
-This example runs broker as part of it's tests. However, if you did wish to create the local broker open a separate terminal in the examples/broker folder and run:
-```bash
-docker-compose up
-```
-
-If you can open a browser to http://localhost and see the broker you have succeeded. You will have to remove the fixture in conftest.py to get this for the consumer to work, however.
-
-## Consumer
-From the root directory run:
-
-```bash
-pytest
-```
-
-Or you can run individual tests like:
-
-```bash
-pytest tests/consumer/test_user_consumer.py::test_get_non_existing_user
-```
-
-To get the consumer to publish a pact to broker you will need to run (2 is an arbitary version number):
-```bash
-pytest tests/consumer/test_user_consumer.py --publish-pact 2
-```
-
-This example is using the python verifier code. There is an example script of how you could run the verifier cli which would be invoked like:
-```bash
-./verify_pact.sh 2
-```
\ No newline at end of file
diff --git a/examples/e2e/conftest.py b/examples/e2e/conftest.py
deleted file mode 100644
index 1ee4c7dc29..0000000000
--- a/examples/e2e/conftest.py
+++ /dev/null
@@ -1,52 +0,0 @@
-
-from testcontainers.compose import DockerCompose
-
-import pytest
-
-
-def pytest_addoption(parser):
-    parser.addoption(
-        "--publish-pact", type=str, action="store",
-        help="Upload generated pact file to pact broker with version"
-    )
-
-    parser.addoption(
-        "--provider-url", type=str, action="store",
-        help="The url to our provider."
-    )
-
-    parser.addoption(
-        "--run-broker", type=bool, action="store",
-        help="Whether to run broker in this test or not."
-    )
-
-
-# This fixture is to simulate a managed Pact Broker or Pactflow account
-# Do not do this yourself but setup one of the above
-# https://github.com/pact-foundation/pact_broker
-@pytest.fixture(scope='session', autouse=True)
-def broker(request):
-    version = request.config.getoption('--publish-pact')
-    publish = True if version else False
-
-    # yield
-    if not publish:
-        yield
-        return
-
-    run_broker = request.config.getoption('--run-broker')
-
-    if not run_broker:
-        yield
-        return
-    else:
-        print('Starting broker')
-        with DockerCompose("../broker",
-                           compose_file_name=["docker-compose.yml"],
-                           pull=True) as compose:
-
-            stdout, stderr = compose.get_logs()
-            if stderr:
-                print("Errors\\n:{}".format(stderr))
-            print(stdout)
-            yield
diff --git a/examples/e2e/pact_provider.py b/examples/e2e/pact_provider.py
deleted file mode 100644
index 52f8eff74a..0000000000
--- a/examples/e2e/pact_provider.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from flask import jsonify, request
-
-from src.provider import app, fakedb
-
-
-@app.route('/_pact/provider_states', methods=['POST'])
-def provider_states():
-    mapping = {'UserA does not exist': setup_no_user_a,
-               'UserA exists and is not an administrator':
-               setup_user_a_nonadmin}
-    mapping[request.json['state']]()
-    return jsonify({'result': request.json['state']})
-
-
-def setup_no_user_a():
-    if 'UserA' in fakedb:
-        del fakedb['UserA']
-
-
-def setup_user_a_nonadmin():
-    id = '00000000-0000-4000-a000-000000000000'
-    some_date = '2016-12-15T20:16:01'
-    ip_address = '198.0.0.1'
-
-    fakedb['UserA'] = {
-        'name': "UserA",
-        'id': id,
-        'created_on': some_date,
-        'ip_address': ip_address,
-        'admin': False
-    }
-
-
-if __name__ == '__main__':
-    app.run(debug=True, port=5001)
diff --git a/examples/e2e/run_pytest.sh b/examples/e2e/run_pytest.sh
deleted file mode 100755
index f1a55023ac..0000000000
--- a/examples/e2e/run_pytest.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-set -o pipefail
-
-FLASK_APP=pact_provider.py python -m flask run -p 5001 & &>/dev/null
-
-FLASK_PID=$!
-
-teardown() {
-  echo "Tearing down Flask server ${FLASK_PID}";
-  kill -9 $FLASK_PID;
-};
-trap teardown EXIT
-
-sleep 3
-
-pytest tests --run-broker True --publish-pact 1
-
-teardown
-
diff --git a/examples/e2e/src/__init__.py b/examples/e2e/src/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/e2e/src/consumer.py b/examples/e2e/src/consumer.py
deleted file mode 100644
index 1381267f86..0000000000
--- a/examples/e2e/src/consumer.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import requests
-from datetime import datetime
-
-
-class UserConsumer(object):
-    def __init__(self, base_uri):
-        self.base_uri = base_uri
-
-    def get_user(self, user_name):
-        """Fetch a user object by user_name from the server."""
-        uri = self.base_uri + '/users/' + user_name
-        response = requests.get(uri)
-        if response.status_code == 404:
-            return None
-
-        name = response.json()['name']
-        created_on = datetime.strptime(response.json()['created_on'], '%Y-%m-%dT%H:%M:%S')
-
-        return User(name, created_on)
-
-
-class User(object):
-    def __init__(self, name, created_on):
-        self.name = name
-        self.created_on = created_on
diff --git a/examples/e2e/src/provider.py b/examples/e2e/src/provider.py
deleted file mode 100644
index fb39459ec1..0000000000
--- a/examples/e2e/src/provider.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from flask import Flask, abort, jsonify
-
-fakedb = {}
-
-app = Flask(__name__)
-
-
-@app.route('/users/<name>')
-def get_user_by_name(name):
-    user_data = fakedb.get(name)
-    if not user_data:
-        abort(404)
-    response = jsonify(**user_data)
-    app.logger.debug('get user for %s returns data:\n%s', name, response.data)
-    return response
-
-
-if __name__ == '__main__':
-    app.run(debug=True, port=5001)
diff --git a/examples/e2e/tests/__init__.py b/examples/e2e/tests/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/e2e/tests/consumer/__init__.py b/examples/e2e/tests/consumer/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/e2e/tests/consumer/test_user_consumer.py b/examples/e2e/tests/consumer/test_user_consumer.py
deleted file mode 100644
index 942f745b9a..0000000000
--- a/examples/e2e/tests/consumer/test_user_consumer.py
+++ /dev/null
@@ -1,83 +0,0 @@
-"""pact test for user service client"""
-
-import logging
-import os
-from datetime import datetime
-
-import pytest
-from pact import Consumer, Like, Provider, Format
-
-from src.consumer import UserConsumer
-
-log = logging.getLogger(__name__)
-logging.basicConfig(level=logging.INFO)
-print(Format().__dict__)
-
-PACT_BROKER_URL = "http://localhost"
-PACT_FILE = "userserviceclient-userservice.json"
-PACT_BROKER_USERNAME = "pactbroker"
-PACT_BROKER_PASSWORD = "pactbroker"
-
-PACT_MOCK_HOST = 'localhost'
-PACT_MOCK_PORT = 1234
-PACT_DIR = os.path.dirname(os.path.realpath(__file__))
-
-
-@pytest.fixture
-def consumer():
-    return UserConsumer(
-        'http://{host}:{port}'
-        .format(host=PACT_MOCK_HOST, port=PACT_MOCK_PORT)
-    )
-
-
-@pytest.fixture(scope='class')
-def pact(request):
-    version = request.config.getoption('--publish-pact')
-    publish = True if version else False
-
-    pact = Consumer('UserServiceClient', version=version).has_pact_with(
-        Provider('UserService'), host_name=PACT_MOCK_HOST, port=PACT_MOCK_PORT,
-        pact_dir=PACT_DIR, publish_to_broker=publish, broker_base_url=PACT_BROKER_URL,
-        broker_username=PACT_BROKER_USERNAME, broker_password=PACT_BROKER_PASSWORD)
-
-    print('start service')
-    pact.start_service()
-
-    yield pact
-    print('stop service')
-    pact.stop_service()
-
-
-def test_get_user_non_admin(broker, pact, consumer):
-    expected = {
-        'name': 'UserA',
-        'id': Format().uuid,
-        'created_on': Format().timestamp,
-        'ip_address': Format().ip_address,
-        'admin': False
-    }
-
-    (pact
-     .given('UserA exists and is not an administrator')
-     .upon_receiving('a request for UserA')
-     .with_request('get', '/users/UserA')
-     .will_respond_with(200, body=Like(expected)))
-
-    with pact:
-        user = consumer.get_user('UserA')
-        assert user.name == 'UserA'
-        assert user.created_on == datetime.strptime('2000-2-1T12:30:00', '%Y-%m-%dT%H:%M:%S')
-
-
-def test_get_non_existing_user(broker, pact, consumer):
-    (pact
-     .given('UserA does not exist')
-     .upon_receiving('a request for UserA')
-     .with_request('get', '/users/UserA')
-     .will_respond_with(404))
-
-    with pact:
-        user = consumer.get_user('UserA')
-        assert user is None
-        pact.verify()
diff --git a/examples/e2e/tests/provider/test_provider.py b/examples/e2e/tests/provider/test_provider.py
deleted file mode 100644
index 59e9a86666..0000000000
--- a/examples/e2e/tests/provider/test_provider.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""pact test for user service client"""
-
-import logging
-import os
-
-from pact import Verifier
-import pytest
-
-log = logging.getLogger(__name__)
-logging.basicConfig(level=logging.INFO)
-
-
-PACT_UPLOAD_URL = (
-    "http://127.0.0.1/pacts/provider/UserService/consumer"
-    "/User_ServiceClient/version"
-)
-PACT_FILE = "userserviceclient-userservice.json"
-PACT_BROKER_URL = "http://localhost"
-PACT_BROKER_USERNAME = "pactbroker"
-PACT_BROKER_PASSWORD = "pactbroker"
-
-PACT_MOCK_HOST = 'localhost'
-PACT_MOCK_PORT = 5001
-PACT_URL = "http://{}:{}".format(PACT_MOCK_HOST, PACT_MOCK_PORT)
-PACT_DIR = os.path.dirname(os.path.realpath(__file__))
-
-
-@pytest.fixture
-def default_opts():
-    return {
-        'broker_username': PACT_BROKER_USERNAME,
-        'broker_password': PACT_BROKER_PASSWORD,
-        'broker_url': PACT_BROKER_URL,
-        'publish_version': '3',
-        'publish_verification_results': False
-    }
-
-
-def test_get_user_non_admin(default_opts):
-    verifier = Verifier(provider='UserService',
-                        provider_base_url=PACT_URL)
-
-    output, logs = verifier.verify_with_broker(**default_opts,
-                                               verbose=True,
-                                               provider_states_setup_url="{}/_pact/provider_states".format(PACT_URL))
-
-    assert (output == 0)
diff --git a/examples/e2e/verify_pact.sh b/examples/e2e/verify_pact.sh
deleted file mode 100755
index 539e081273..0000000000
--- a/examples/e2e/verify_pact.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-set -o pipefail
-
-python pact_provider.py & &>/dev/null
-FLASK_PID=$!
-VERSION=$1
-
-function teardown {
-  echo "Tearing down Flask server ${FLASK_PID}"
-  CHILD=`pgrep -P $FLASK_PID`
-  echo "Kill provider app with pid $CHILD"
-
-  kill -9 $CHILD
-}
-trap teardown EXIT
-
-if [ -x $VERSION ];
-then
-  echo "Validating provider locally"
-
-  pact-verifier --provider-base-url=http://localhost:5001 \
-    --provider-states-setup-url=http://localhost:5001/_pact/provider_states \
-    ./userserviceclient-userservice.json
-else
-
-  pact-verifier --provider-base-url=http://localhost:5001 \
-    --provider-app-version $VERSION \
-    --pact-url="http://127.0.0.1/pacts/provider/UserService/consumer/UserServiceClient/latest" \
-    --pact-broker-username pactbroker \
-    --pact-broker-password pactbroker \
-    --publish-verification-results \
-    --provider-states-setup-url=http://localhost:5001/_pact/provider_states
-fi
diff --git a/examples/fastapi_e2e/README.md b/examples/fastapi_e2e/README.md
deleted file mode 100644
index aef04a66f6..0000000000
--- a/examples/fastapi_e2e/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Introduction
-
-This is an e2e example to show the provider verification working for both cli and python api.
-
-## Setup
-
-Create your own virtualenv for this. Run
-
-```bash
-pip install -r requirements.txt
-pip install ../../
-pytest
-```
-
-This should provide you with a relative path to pact install relatively (2 dirs up)
-
-Create the local broker (for demo purposes only) Open a separate terminal in the examples/broker folder and run:
-```bash
-docker-compose up
-```
-
-If you can open a browser to http://localhost and see the broker you have succeeded.
-
-## Consumer
-
-From the root directory run:
-
-```bash
-pytest
-```
-
-Or you can run individual tests like:
-
-```bash
-pytest tests/consumer/test_user_consumer.py::test_get_non_existing_user
-```
-
-To get the consumer to publish a pact to broker you will need to run (2 is an arbitary version number):
-```bash
-pytest tests/consumer/test_user_consumer.py --publish-pact 2
-```
-
-And then you can run:
-```bash
-./verify_pact.sh 2
-```
-
-To verify the pact
diff --git a/examples/fastapi_e2e/__init__.py b/examples/fastapi_e2e/__init__.py
deleted file mode 100644
index 4610327596..0000000000
--- a/examples/fastapi_e2e/__init__.py
+++ /dev/null
@@ -1,1 +0,0 @@
-"""fastapi package info."""
diff --git a/examples/fastapi_e2e/requirements.txt b/examples/fastapi_e2e/requirements.txt
deleted file mode 100644
index a414b9e63b..0000000000
--- a/examples/fastapi_e2e/requirements.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-fastapi==0.65.2
-pytest==5.4.1
-requests==2.23.0
-uvicorn==0.12.3
diff --git a/examples/fastapi_e2e/run_pytest.sh b/examples/fastapi_e2e/run_pytest.sh
deleted file mode 100755
index 08b70cdeaa..0000000000
--- a/examples/fastapi_e2e/run_pytest.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-set -o pipefail
-
-
-
-FLASK_APP=pact_provider.py python -m flask run -p 1235 & &>/dev/null
-
-# python pact_provider.py & &>/dev/null
-FLASK_PID=$!
-
-function teardown {
-  echo "Tearing down Flask server ${FLASK_PID}"
-
-  kill -9 $FLASK_PID
-}
-trap teardown EXIT
-
-sleep 3
-
-pytest
-
-teardown()
-
diff --git a/examples/fastapi_e2e/src/__init__.py b/examples/fastapi_e2e/src/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/fastapi_e2e/src/consumer.py b/examples/fastapi_e2e/src/consumer.py
deleted file mode 100644
index 8bd15fccfc..0000000000
--- a/examples/fastapi_e2e/src/consumer.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import requests
-
-
-class UserConsumer(object):
-    def __init__(self, base_uri):
-        self.base_uri = base_uri
-
-    def get_user(self, user_name):
-        """Fetch a user object by user_name from the server."""
-        uri = self.base_uri + '/users/' + user_name
-        response = requests.get(uri)
-        if response.status_code == 404:
-            return None
-
-        name = response.json()['name']
-        return User(name)
-
-
-class User(object):
-    def __init__(self, name):
-        self.name = name
diff --git a/examples/fastapi_e2e/tests/__init__.py b/examples/fastapi_e2e/tests/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/fastapi_e2e/tests/consumer/__init__.py b/examples/fastapi_e2e/tests/consumer/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/fastapi_e2e/tests/consumer/test_user_consumer.py b/examples/fastapi_e2e/tests/consumer/test_user_consumer.py
deleted file mode 100644
index 590a3e6bad..0000000000
--- a/examples/fastapi_e2e/tests/consumer/test_user_consumer.py
+++ /dev/null
@@ -1,85 +0,0 @@
-"""pact test for user service client"""
-
-import logging
-import os
-import atexit
-
-import pytest
-from pact import Consumer, Like, Provider, Term, Format
-
-from ...src.consumer import UserConsumer
-
-log = logging.getLogger(__name__)
-logging.basicConfig(level=logging.INFO)
-
-PACT_BROKER_URL = "http://localhost"
-PACT_FILE = "userserviceclient-userservice.json"
-PACT_BROKER_USERNAME = "pactbroker"
-PACT_BROKER_PASSWORD = "pactbroker"
-
-PACT_MOCK_HOST = 'localhost'
-PACT_MOCK_PORT = 1234
-PACT_DIR = os.path.dirname(os.path.realpath(__file__))
-
-
-@pytest.fixture
-def consumer():
-    return UserConsumer(
-        'http://{host}:{port}'
-        .format(host=PACT_MOCK_HOST, port=PACT_MOCK_PORT)
-    )
-
-
-@pytest.fixture(scope='session')
-def pact(request):
-    version = request.config.getoption('--publish-pact')
-    publish = True if version else False
-
-    pact = Consumer('UserServiceClient', version=version).has_pact_with(
-        Provider('UserService'), host_name=PACT_MOCK_HOST, port=PACT_MOCK_PORT,
-        pact_dir=PACT_DIR, publish_to_broker=publish, broker_base_url=PACT_BROKER_URL,
-        broker_username=PACT_BROKER_USERNAME, broker_password=PACT_BROKER_PASSWORD)
-
-    print('start service')
-    pact.start_service()
-    atexit.register(pact.stop_service)
-
-    yield pact
-    print('stop service')
-    pact.stop_service()
-
-
-def test_get_user_non_admin(pact, consumer):
-    expected = {
-        'name': 'UserA',
-        'id': Format().uuid,
-        'created_on': Term(
-            r'\d+-\d+-\d+T\d+:\d+:\d+',
-            '2016-12-15T20:16:01'
-        ),
-        'ip_address': Format().ip_address,
-        'admin': False
-    }
-
-    (pact
-     .given('UserA exists and is not an administrator')
-     .upon_receiving('a request for UserA')
-     .with_request('get', '/users/UserA')
-     .will_respond_with(200, body=Like(expected)))
-
-    with pact:
-        user = consumer.get_user('UserA')
-        assert user.name == 'UserA'
-
-
-def test_get_non_existing_user(pact, consumer):
-    (pact
-     .given('UserA does not exist')
-     .upon_receiving('a request for UserA')
-     .with_request('get', '/users/UserA')
-     .will_respond_with(404))
-
-    with pact:
-        user = consumer.get_user('UserA')
-        assert user is None
-    # pact.verify()
diff --git a/examples/fastapi_e2e/tests/provider/__init__.py b/examples/fastapi_e2e/tests/provider/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/examples/fastapi_e2e/userserviceclient-userservice.json b/examples/fastapi_e2e/userserviceclient-userservice.json
deleted file mode 100644
index 5453494e22..0000000000
--- a/examples/fastapi_e2e/userserviceclient-userservice.json
+++ /dev/null
@@ -1,65 +0,0 @@
-{
-  "consumer": {
-    "name": "UserServiceClient"
-  },
-  "provider": {
-    "name": "UserService"
-  },
-  "interactions": [
-    {
-      "description": "a request for UserA",
-      "providerState": "UserA exists and is not an administrator",
-      "request": {
-        "method": "get",
-        "path": "/users/UserA"
-      },
-      "response": {
-        "status": 200,
-        "headers": {
-        },
-        "body": {
-          "name": "UserA",
-          "id": "fc763eba-0905-41c5-a27f-3934ab26786c",
-          "created_on": "2016-12-15T20:16:01",
-          "ip_address": "127.0.0.1",
-          "admin": false
-        },
-        "matchingRules": {
-          "$.body": {
-            "match": "type"
-          },
-          "$.body.id": {
-            "match": "regex",
-            "regex": "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
-          },
-          "$.body.created_on": {
-            "match": "regex",
-            "regex": "\\d+-\\d+-\\d+T\\d+:\\d+:\\d+"
-          },
-          "$.body.ip_address": {
-            "match": "regex",
-            "regex": "(\\d{1,3}\\.)+\\d{1,3}"
-          }
-        }
-      }
-    },
-    {
-      "description": "a request for UserA",
-      "providerState": "UserA does not exist",
-      "request": {
-        "method": "get",
-        "path": "/users/UserA"
-      },
-      "response": {
-        "status": 404,
-        "headers": {
-        }
-      }
-    }
-  ],
-  "metadata": {
-    "pactSpecification": {
-      "version": "2.0.0"
-    }
-  }
-}
\ No newline at end of file
diff --git a/examples/fastapi_provider/run_pytest.sh b/examples/fastapi_provider/run_pytest.sh
new file mode 100755
index 0000000000..c93b905519
--- /dev/null
+++ b/examples/fastapi_provider/run_pytest.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+set -o pipefail
+
+uvicorn src.provider:app --host 0.0.0.0 --port 8080 & &>/dev/null
+FASTAPI_PID=$!
+
+function teardown {
+  echo "Tearing down FastAPI server: ${FASTAPI_PID}"
+  kill -9 $FLASK_PID
+}
+trap teardown EXIT
+
+sleep 1
+
+pytest
\ No newline at end of file
diff --git a/examples/fastapi_e2e/tests/conftest.py b/examples/fastapi_provider/tests/conftest.py
similarity index 100%
rename from examples/fastapi_e2e/tests/conftest.py
rename to examples/fastapi_provider/tests/conftest.py
diff --git a/examples/fastapi_e2e/tests/fastapi_provider.py b/examples/fastapi_provider/tests/fastapi_provider.py
similarity index 100%
rename from examples/fastapi_e2e/tests/fastapi_provider.py
rename to examples/fastapi_provider/tests/fastapi_provider.py
diff --git a/examples/fastapi_e2e/tests/provider/test_provider.py b/examples/fastapi_provider/tests/provider/test_provider.py
similarity index 60%
rename from examples/fastapi_e2e/tests/provider/test_provider.py
rename to examples/fastapi_provider/tests/provider/test_provider.py
index 3697472c91..b83d2330c0 100644
--- a/examples/fastapi_e2e/tests/provider/test_provider.py
+++ b/examples/fastapi_provider/tests/provider/test_provider.py
@@ -11,16 +11,13 @@
 log = logging.getLogger(__name__)
 logging.basicConfig(level=logging.INFO)
 
-PACT_UPLOAD_URL = (
-    "http://127.0.0.1/pacts/provider/UserService/consumer"
-    "/User_ServiceClient/version"
-)
+PACT_UPLOAD_URL = "http://127.0.0.1/pacts/provider/UserService/consumer" "/User_ServiceClient/version"
 PACT_FILE = "userserviceclient-userservice.json"
 PACT_BROKER_URL = "http://localhost"
 PACT_BROKER_USERNAME = "pactbroker"
 PACT_BROKER_PASSWORD = "pactbroker"
 
-PACT_MOCK_HOST = 'localhost'
+PACT_MOCK_HOST = "localhost"
 PACT_MOCK_PORT = 8000
 PACT_URL = "http://{}:{}".format(PACT_MOCK_HOST, PACT_MOCK_PORT)
 PACT_DIR = os.path.dirname(os.path.realpath(__file__))
@@ -35,11 +32,12 @@ def server():
 
 
 def test_get_user_non_admin(server):
-    verifier = Verifier(provider='UserService',
-                        provider_base_url=PACT_URL)
+    verifier = Verifier(provider="UserService", provider_base_url=PACT_URL)
 
-    output, _ = verifier.verify_pacts('./userserviceclient-userservice.json',
-                                      verbose=False,
-                                      provider_states_setup_url="{}/_pact/provider_states".format(PACT_URL))
+    output, _ = verifier.verify_pacts(
+        "./userserviceclient-userservice.json",
+        verbose=False,
+        provider_states_setup_url="{}/_pact/provider_states".format(PACT_URL),
+    )
 
-    assert (output == 0)
+    assert output == 0
diff --git a/examples/flask_provider/run_pytest.sh b/examples/flask_provider/run_pytest.sh
new file mode 100755
index 0000000000..ca022085f1
--- /dev/null
+++ b/examples/flask_provider/run_pytest.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+set -o pipefail
+
+# Run the Flask server, using the pact_provider.py as the app to be able to
+# inject the provider_states endpoint
+FLASK_APP=tests/pact_provider.py python -m flask run -p 5001 &
+FLASK_PID=$!
+
+# Make sure the Flask server is stopped when finished to avoid blocking the port
+function teardown {
+  echo "Tearing down Flask server: ${FLASK_PID}"
+  kill -9 $FLASK_PID
+}
+trap teardown EXIT
+
+# Wait a little in case Flask isn't quite ready
+sleep 1
+
+# Now run the tests
+pytest tests --run-broker True --publish-pact 1
\ No newline at end of file
diff --git a/examples/flask_provider/tests/conftest.py b/examples/flask_provider/tests/conftest.py
new file mode 100644
index 0000000000..c200399206
--- /dev/null
+++ b/examples/flask_provider/tests/conftest.py
@@ -0,0 +1,87 @@
+import pathlib
+
+import docker
+import pytest
+from testcontainers.compose import DockerCompose
+
+
+def pytest_addoption(parser):
+    parser.addoption(
+        "--publish-pact", type=str, action="store", help="Upload generated pact file to pact broker with version"
+    )
+
+    parser.addoption("--run-broker", type=bool, action="store", help="Whether to run broker in this test or not.")
+
+
+@pytest.fixture(scope="session", autouse=True)
+def publish_existing_pact(broker):
+    """Publish the contents of the pacts folder to the Pact Broker.
+
+    In normal usage, a Consumer would publish Pacts to the Pact Broker after
+    running tests - this fixture would NOT be needed.
+    .
+    Because the broker is being used standalone here, it will not contain the
+    required Pacts, so we must first spin up the pact-cli and publish them.
+
+    In the Pact Broker logs, this corresponds to the following entry:
+      PactBroker::Pacts::Service -- Creating new pact publication with params \
+      {:consumer_name=>"UserServiceClient", :provider_name=>"UserService", \
+      :revision_number=>nil, :consumer_version_number=>"1", :pact_version_sha=>nil, \
+      :consumer_name_in_pact=>"UserServiceClient", :provider_name_in_pact=>"UserService"}
+    """
+    source = str(pathlib.Path.cwd().joinpath("..", "pacts").resolve())
+    pacts = [f"{source}:/pacts"]
+    envs = {
+        "PACT_BROKER_BASE_URL": "http://broker_app:9292",
+        "PACT_BROKER_USERNAME": "pactbroker",
+        "PACT_BROKER_PASSWORD": "pactbroker",
+    }
+
+    client = docker.from_env()
+
+    print("Publishing existing Pact")
+    client.containers.run(
+        remove=True,
+        network="broker_default",
+        volumes=pacts,
+        image="pactfoundation/pact-cli:latest",
+        environment=envs,
+        command="publish /pacts --consumer-app-version 1",
+    )
+    print("Finished publishing")
+
+
+# This fixture is to simulate a managed Pact Broker or Pactflow account.
+# For almost all purposes outside this example, you will want to use a real
+# broker. See https://github.com/pact-foundation/pact_broker for further details.
+@pytest.fixture(scope="session", autouse=True)
+def broker(request):
+    version = request.config.getoption("--publish-pact")
+    publish = True if version else False
+
+    # If the results are not going to be published to the broker, there is
+    # nothing further to do anyway
+    if not publish:
+        yield
+        return
+
+    run_broker = request.config.getoption("--run-broker")
+
+    if run_broker:
+        # Start up the broker using docker-compose
+        print("Starting broker")
+        with DockerCompose("../broker", compose_file_name=["docker-compose.yml"], pull=True) as compose:
+            stdout, stderr = compose.get_logs()
+            if stderr:
+                print("Errors\\n:{}".format(stderr))
+            print("{}".format(stdout))
+            print("Started broker")
+
+            yield
+            print("Stopping broker")
+        print("Broker stopped")
+    else:
+        # Assuming there is a broker available already, docker-compose has been
+        # used manually as the --run-broker option has not been provided
+        yield
+        return
diff --git a/examples/flask_provider/tests/pact_provider.py b/examples/flask_provider/tests/pact_provider.py
new file mode 100644
index 0000000000..0cb9674862
--- /dev/null
+++ b/examples/flask_provider/tests/pact_provider.py
@@ -0,0 +1,48 @@
+"""additional endpoints to facilitate provider_states"""
+
+from flask import jsonify, request
+
+from src.provider import app, fakedb
+
+
+@app.route("/_pact/provider_states", methods=["POST"])
+def provider_states():
+    """Implement the "functionality" to change the state, to prepare for a test.
+
+    When a Pact interaction is verified, it provides the "given" part of the
+    description from the Consumer in the X_PACT_PROVIDER_STATES header.
+    This can then be used to perform some operations on a database for example,
+    so that the actual request can be performed and respond as expected.
+    See: https://docs.pact.io/getting_started/provider_states
+
+    This provider_states endpoint is deemed test only, and generally should not
+    be available once deployed to an environment. It would represent both a
+    potential data loss risk, as well as a security risk.
+
+    As such, when running the Provider to test against, this is defined as the
+    FLASK_APP to run, adding this additional route to the app while keeping the
+    source separate.
+    """
+    mapping = {
+        "UserA does not exist": setup_no_user_a,
+        "UserA exists and is not an administrator": setup_user_a_nonadmin,
+    }
+    mapping[request.json["state"]]()
+    return jsonify({"result": request.json["state"]})
+
+
+def setup_no_user_a():
+    if "UserA" in fakedb:
+        del fakedb["UserA"]
+
+
+def setup_user_a_nonadmin():
+    id = "00000000-0000-4000-a000-000000000000"
+    some_date = "2016-12-15T20:16:01"
+    ip_address = "198.0.0.1"
+
+    fakedb["UserA"] = {"name": "UserA", "id": id, "created_on": some_date, "ip_address": ip_address, "admin": False}
+
+
+if __name__ == "__main__":
+    app.run(debug=True, port=5001)
diff --git a/examples/flask_provider/tests/provider/test_provider.py b/examples/flask_provider/tests/provider/test_provider.py
new file mode 100644
index 0000000000..bd599389ce
--- /dev/null
+++ b/examples/flask_provider/tests/provider/test_provider.py
@@ -0,0 +1,83 @@
+"""pact test for user service provider"""
+
+import logging
+
+import pytest
+
+from pact import Verifier
+
+log = logging.getLogger(__name__)
+logging.basicConfig(level=logging.INFO)
+
+# For the purposes of this example, the broker is started up as a fixture defined
+# in conftest.py. For normal usage this would be self-hosted or using Pactflow.
+PACT_BROKER_URL = "http://localhost"
+PACT_BROKER_USERNAME = "pactbroker"
+PACT_BROKER_PASSWORD = "pactbroker"
+
+# For the purposes of this example, the Flask provider will be started up as part
+# of run_pytest.sh when running the tests. Alternatives could be, for example
+# running a Docker container with a database of test data configured.
+# This is the "real" provider to verify against.
+PROVIDER_HOST = "localhost"
+PROVIDER_PORT = 5001
+PROVIDER_URL = f"http://{PROVIDER_HOST}:{PROVIDER_PORT}"
+
+
+@pytest.fixture
+def broker_opts():
+    return {
+        "broker_username": PACT_BROKER_USERNAME,
+        "broker_password": PACT_BROKER_PASSWORD,
+        "broker_url": PACT_BROKER_URL,
+        "publish_version": "3",
+        "publish_verification_results": True,
+    }
+
+
+def test_user_service_provider_against_broker(broker_opts):
+    verifier = Verifier(provider="UserService", provider_base_url=PROVIDER_URL)
+
+    # Request all Pact(s) from the Pact Broker to verify this Provider against.
+    # In the Pact Broker logs, this corresponds to the following entry:
+    # PactBroker::Api::Resources::ProviderPactsForVerification -- Fetching pacts for verification by UserService -- {:provider_name=>"UserService", :params=>{}}
+    success, logs = verifier.verify_with_broker(
+        **broker_opts,
+        verbose=True,
+        provider_states_setup_url=f"{PROVIDER_URL}/_pact/provider_states",
+        enable_pending=False,
+    )
+    # If publish_verification_results is set to True, the results will be
+    # published to the Pact Broker.
+    # In the Pact Broker logs, this corresponds to the following entry:
+    #   PactBroker::Verifications::Service -- Creating verification 200 for \
+    #   pact_version_sha=c8568cbb30d2e3933b2df4d6e1248b3d37f3be34 -- \
+    #   {"success"=>true, "providerApplicationVersion"=>"3", "wip"=>false, \
+    #   "pending"=>"true"}
+
+    # Note:
+    #  If "successful", then the return code here will be 0
+    #  This can still be 0 and so PASS if a Pact verification FAILS, as long as
+    #  it has not resulted in a REGRESSION of an already verified interaction.
+    #  See https://docs.pact.io/pact_broker/advanced_topics/pending_pacts/ for
+    #  more details.
+    assert success == 0
+
+
+def test_user_service_provider_against_pact():
+    verifier = Verifier(provider="UserService", provider_base_url=PROVIDER_URL)
+
+    # Rather than requesting the Pact interactions from the Pact Broker, this
+    # will perform the verification based on the Pact file locally.
+    #
+    # Because there is no way of knowing the previous state of an interaction,
+    # if it has been successful in the past (since this is what the Pact Broker
+    # is for), if the verification of an interaction fails then the success
+    # result will be != 0, and so the test will FAIL.
+    output, _ = verifier.verify_pacts(
+        "../pacts/userserviceclient-userservice.json",
+        verbose=False,
+        provider_states_setup_url="{}/_pact/provider_states".format(PROVIDER_URL),
+    )
+
+    assert output == 0
diff --git a/examples/message/conftest.py b/examples/message/conftest.py
index 1ee4c7dc29..5e54cffc78 100644
--- a/examples/message/conftest.py
+++ b/examples/message/conftest.py
@@ -1,4 +1,3 @@
-
 from testcontainers.compose import DockerCompose
 
 import pytest
@@ -6,27 +5,20 @@
 
 def pytest_addoption(parser):
     parser.addoption(
-        "--publish-pact", type=str, action="store",
-        help="Upload generated pact file to pact broker with version"
+        "--publish-pact", type=str, action="store", help="Upload generated pact file to pact broker with version"
     )
 
-    parser.addoption(
-        "--provider-url", type=str, action="store",
-        help="The url to our provider."
-    )
+    parser.addoption("--provider-url", type=str, action="store", help="The url to our provider.")
 
-    parser.addoption(
-        "--run-broker", type=bool, action="store",
-        help="Whether to run broker in this test or not."
-    )
+    parser.addoption("--run-broker", type=bool, action="store", help="Whether to run broker in this test or not.")
 
 
 # This fixture is to simulate a managed Pact Broker or Pactflow account
 # Do not do this yourself but setup one of the above
 # https://github.com/pact-foundation/pact_broker
-@pytest.fixture(scope='session', autouse=True)
+@pytest.fixture(scope="session", autouse=True)
 def broker(request):
-    version = request.config.getoption('--publish-pact')
+    version = request.config.getoption("--publish-pact")
     publish = True if version else False
 
     # yield
@@ -34,16 +26,14 @@ def broker(request):
         yield
         return
 
-    run_broker = request.config.getoption('--run-broker')
+    run_broker = request.config.getoption("--run-broker")
 
     if not run_broker:
         yield
         return
     else:
-        print('Starting broker')
-        with DockerCompose("../broker",
-                           compose_file_name=["docker-compose.yml"],
-                           pull=True) as compose:
+        print("Starting broker")
+        with DockerCompose("../broker", compose_file_name=["docker-compose.yml"], pull=True) as compose:
 
             stdout, stderr = compose.get_logs()
             if stderr:
diff --git a/examples/message/tests/__init__.py b/examples/message/tests/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000