From 4efc5d5b18d58dc15660d5308a84e1db6222fc0b Mon Sep 17 00:00:00 2001 From: Mike Geeves Date: Tue, 21 Dec 2021 15:08:59 +0000 Subject: [PATCH 1/3] test(examples): move shared fixtures to a common folder so they can be reused, remove repeated code --- examples/README.md | 9 ++ examples/common/sharedfixtures.py | 88 ++++++++++++++++++ examples/fastapi_provider/tests/conftest.py | 98 +++------------------ examples/flask_provider/tests/conftest.py | 91 ++----------------- examples/message/conftest.py | 46 ++-------- 5 files changed, 122 insertions(+), 210 deletions(-) create mode 100644 examples/common/sharedfixtures.py diff --git a/examples/README.md b/examples/README.md index e74683017..406f45719 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,6 +4,7 @@ * [Overview](#overview) * [broker](#broker) + * [common](#common) * [consumer](#consumer) * [flask_provider](#flask_provider) * [fastapi_provider](#fastapi_provider) @@ -41,6 +42,13 @@ 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. +## common + +To avoid needing to duplicate certain fixtures, such as starting up a docker based Pact broker (to demonstrate how the +test process could work), the shared fixtures used by the pytests have all been placed into a single location.] +This means it is easier to see the relevant code for the example without having to go through the boilerplate fixtures. +See [Requiring/Loading plugins in a test module or conftest file] for further details of this approach. + ## consumer Pact is consumer-driven, which means first the contracts are created. These Pact contracts are generated during @@ -205,3 +213,4 @@ without a [Pact Broker]. [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 +[Requiring/Loading plugins in a test module or conftest file]: https://docs.pytest.org/en/6.2.x/writing_plugins.html#requiring-loading-plugins-in-a-test-module-or-conftest-file \ No newline at end of file diff --git a/examples/common/sharedfixtures.py b/examples/common/sharedfixtures.py new file mode 100644 index 000000000..6e1b937ca --- /dev/null +++ b/examples/common/sharedfixtures.py @@ -0,0 +1,88 @@ +import pathlib + +import docker +import pytest +from testcontainers.compose import DockerCompose + + +# 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 + + +@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") + + +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.") + parser.addoption("--provider-url", type=str, action="store", help="The url to our provider.") diff --git a/examples/fastapi_provider/tests/conftest.py b/examples/fastapi_provider/tests/conftest.py index 9b5437366..903066f35 100644 --- a/examples/fastapi_provider/tests/conftest.py +++ b/examples/fastapi_provider/tests/conftest.py @@ -1,9 +1,15 @@ -import pathlib +# Load in the fixtures from common/sharedfixtures.py +import sys + +sys.path.append("../common") + +pytest_plugins = [ + "sharedfixtures", +] + from multiprocessing import Process -import docker import pytest -from testcontainers.compose import DockerCompose from .pact_provider import run_server @@ -15,86 +21,8 @@ def server(): yield proc # Cleanup after test - proc.kill() - - -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") + if sys.version_info >= (3, 7): + # multiprocessing.kill is new in 3.7 + proc.kill() 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 + proc.terminate() diff --git a/examples/flask_provider/tests/conftest.py b/examples/flask_provider/tests/conftest.py index c20039920..ca4dea9cd 100644 --- a/examples/flask_provider/tests/conftest.py +++ b/examples/flask_provider/tests/conftest.py @@ -1,87 +1,8 @@ -import pathlib +# Load in the fixtures from common/sharedfixtures.py +import sys -import docker -import pytest -from testcontainers.compose import DockerCompose +sys.path.append("../common") - -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 +pytest_plugins = [ + "sharedfixtures", +] diff --git a/examples/message/conftest.py b/examples/message/conftest.py index 5e54cffc7..ca4dea9cd 100644 --- a/examples/message/conftest.py +++ b/examples/message/conftest.py @@ -1,42 +1,8 @@ -from testcontainers.compose import DockerCompose +# Load in the fixtures from common/sharedfixtures.py +import sys -import pytest +sys.path.append("../common") - -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 +pytest_plugins = [ + "sharedfixtures", +] From c568afe9a6e0af6215f4296a9dd32b2f2d2b6838 Mon Sep 17 00:00:00 2001 From: Mike Geeves Date: Tue, 21 Dec 2021 15:16:46 +0000 Subject: [PATCH 2/3] test(examples): move shared fixtures to a common folder so they can be reused, remove repeated code --- examples/fastapi_provider/tests/conftest.py | 13 ++++++------- examples/flask_provider/tests/conftest.py | 2 +- examples/message/conftest.py | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/fastapi_provider/tests/conftest.py b/examples/fastapi_provider/tests/conftest.py index 903066f35..6e3176f20 100644 --- a/examples/fastapi_provider/tests/conftest.py +++ b/examples/fastapi_provider/tests/conftest.py @@ -1,18 +1,17 @@ -# Load in the fixtures from common/sharedfixtures.py import sys +from multiprocessing import Process + +import pytest +from .pact_provider import run_server + +# Load in the fixtures from common/sharedfixtures.py sys.path.append("../common") pytest_plugins = [ "sharedfixtures", ] -from multiprocessing import Process - -import pytest - -from .pact_provider import run_server - @pytest.fixture(scope="module") def server(): diff --git a/examples/flask_provider/tests/conftest.py b/examples/flask_provider/tests/conftest.py index ca4dea9cd..90ac63b9a 100644 --- a/examples/flask_provider/tests/conftest.py +++ b/examples/flask_provider/tests/conftest.py @@ -1,6 +1,6 @@ -# Load in the fixtures from common/sharedfixtures.py import sys +# Load in the fixtures from common/sharedfixtures.py sys.path.append("../common") pytest_plugins = [ diff --git a/examples/message/conftest.py b/examples/message/conftest.py index ca4dea9cd..90ac63b9a 100644 --- a/examples/message/conftest.py +++ b/examples/message/conftest.py @@ -1,6 +1,6 @@ -# Load in the fixtures from common/sharedfixtures.py import sys +# Load in the fixtures from common/sharedfixtures.py sys.path.append("../common") pytest_plugins = [ From 50fc4a255677763dc0fe93cf9064d444d2f6ef15 Mon Sep 17 00:00:00 2001 From: Mike Geeves Date: Fri, 31 Dec 2021 21:00:00 +0000 Subject: [PATCH 3/3] test(examples): move shared fixtures to a common folder so they can be reused, remove repeated code --- examples/consumer/conftest.py | 53 ++++------------------------------- 1 file changed, 6 insertions(+), 47 deletions(-) diff --git a/examples/consumer/conftest.py b/examples/consumer/conftest.py index 252c027ca..90ac63b9a 100644 --- a/examples/consumer/conftest.py +++ b/examples/consumer/conftest.py @@ -1,49 +1,8 @@ -import pytest -from testcontainers.compose import DockerCompose +import sys +# Load in the fixtures from common/sharedfixtures.py +sys.path.append("../common") -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 +pytest_plugins = [ + "sharedfixtures", +]