From c0523b29052f596c6e0d06e936f5322266887725 Mon Sep 17 00:00:00 2001 From: Greg Gauthier Date: Thu, 23 May 2019 13:19:43 +0100 Subject: [PATCH] readme and refactoring --- README.md | 29 ++++++++++++++++++++++++ app/simple.py | 1 - install.sh | 53 ++++++++++++++++++++++++++++++++++++++++++++ rundemo.sh | 3 +++ tests/test_hov.py | 32 ++++++++++++++++++++------ tests/test_simple.py | 5 ++--- 6 files changed, 112 insertions(+), 11 deletions(-) create mode 100755 install.sh create mode 100755 rundemo.sh diff --git a/README.md b/README.md index e69de29..5208781 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,29 @@ +# API TESTING WITH HOVERFLY (HoverPy) + +### WHAT + +This is a very simple project to demonstrate the perported uses of HoverFly (an api mocking/simulation tool). In this case, the testing use case is demonstrated by way of a custom pytest suite found in the tests directory. + +### HOW + +1> Run the included `install.sh` script. This will: +* confirm that you have python3 installed and ready for use +* confirm that you have the HoverFly CLI installed and ready for use +* confirm that you have pipenv for python3, and configure your project virtual environment + +2> Run the included `rundemo.sh` script, after install. This will: +* use the pipenv environment to execute the hoverfly tests with pytest. What are these tests doing? 1: fire up the flask app. 2: hit both the GET endpoint and the POST endpoint, and execute a simple assert on the response JSONs. The test is actually using the stored session data for its comparison, rather than testing within the session itself. The suite deletes the session data files, when all tests are complete. + +### DETAILS + +The `/app` directory contains a very simple Flask server app, designed to emulate a live application with available api endpoints. It's useless beyond the scope of this demo. It models two simple GET requests, and a simple POST request, both of which return json responses as many of our service APIs do. + +The `/tests` directory contains a few standard Flask app unit tests, merely for reference. The main event, is the `test_hov.py` file, which contains the test code that includes the use of HoverFly capture/simulate code. It's actually a bit underwhelming, because of how easy it is to use in tests. + +The point of having both sets of tests, is to show the difference between what a set of unit tests on an api project would look like, compared with a separate set of hoverfly api tests, which would be written to talk directly to a live api (note how the hoverfly tests require me to start the server in the setup). + +I'm not sure if the hoverfly CLI binaries are actually necessary for the python bindings to work, but I added the installation of them here anyway, just to be sure, and to provide some documentation for future installations. + +Why does it matter that we're using stored session data, rather than executing asserts during an active session? Well, for one thing, the stored session data could be used to execute mocked tests at a later date (the server need not be live). Or, mock sessions could be used to model CONTRACTS. Or, they could be used for ongoing development on a new endpoint. + +More experimentation needs to be done, to determine the full value of the tool. diff --git a/app/simple.py b/app/simple.py index 79cc54d..4a87b35 100644 --- a/app/simple.py +++ b/app/simple.py @@ -1,4 +1,3 @@ -import threading from random import randint from secrets import choice from string import ascii_letters diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..b01763f --- /dev/null +++ b/install.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env sh + +OS=$(uname) +PY=$(which python3) +HC=$(which hoverctl) + +echo "PREPARING PYTHON3 ENVIRONMENT..." +if [[ "$PY" = "python3 not found" ]]; + then + echo "No python3 installation found. Attempting install now..." + if [[ "$OS" = "Darwin" ]]; then + $(brew install python3) + fi + if [[ "$OS" = "Linux" ]]; then + $(sudo apt install python3) + fi + PY=$(which python3) +fi + +echo "PREPARING HOVERFLY INSTALLATION..." +if [[ "$HC" = "hoverctl not found" ]]; + then + echo "HoverFly CLI binary not found. Attempting install now..." + if [[ "$OS" = "Darwin" ]]; then + $(brew install hoverfly) + fi + if [[ "$OS" = "Linux" ]]; then + $(wget https://github.com/SpectoLabs/hoverfly/releases/download/v1.0.0/hoverfly_bundle_linux_amd64.zip) + $(unzip hoverfly_bundle_linux_amd64.zip) + $(sudo mv hoverctl /usr/local/bin) + $(sudo mv hoverfly /usr/local/bin) + fi + HC=$(which hoverctl) +fi + +echo "PREPARING PROJECT VIRTUAL ENVIRONMENT..." +PENV=$(${PY} -m pip freeze|grep -i pipenv) +PYVER=$(${PY} --version) +echo "${PYVER}" + +if [[ -z "$PENV" && "$PENV"=" " ]]; + then + echo "Installing pipenv for python3..." + ${PY} -m pip install pipenv + echo "Installing pipenv virtual environment..." + pipenv install + else + echo "\t${PENV}" +fi +echo "Dependency Graph:" +pipenv graph + +echo "ALL SYSTEMS GO!" \ No newline at end of file diff --git a/rundemo.sh b/rundemo.sh new file mode 100755 index 0000000..479aa50 --- /dev/null +++ b/rundemo.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +pipenv run python -m pytest tests/test_hov.py --verbose --show-capture=all \ No newline at end of file diff --git a/tests/test_hov.py b/tests/test_hov.py index 4712d10..f552fcc 100644 --- a/tests/test_hov.py +++ b/tests/test_hov.py @@ -6,21 +6,39 @@ from hoverpy import capture class TestUsingHoverpy: + root_url = "http://127.0.0.1:5000" + def setup_class(self): # startup the simple server (only needed for this example) + # This will get destroyed automatically, when the tests end Popen("sh pipenv run python app/simple.py", shell=True, stdout=PIPE, stderr=PIPE) def teardown_class(self): - # deletes the captured session + # deletes the captured sessions os.remove("tests/test_version_call.db") + os.remove("tests/test_random_numbers.db") + os.remove("tests/test_random_string.db") + os.remove("tests/test_hashname.db") @capture("tests/test_version_call.db", recordMode="once") def test_version_call(self): - resp = requests.get("http://127.0.0.1:5000/version") + resp = requests.get(self.root_url + "/version") assert resp.json()['version'] == 0.1 - # FROM THE DEMO: - # @capture("tests/test_time2.db", recordMode="once") - # def test_time3(self): - # time = requests.get("http://time.jsontest.com") - # assert list(time.json().keys()).index('time') > 0 + @capture("tests/test_random_numbers.db", recordMode="once") + def test_random_numbers(self): + resp = requests.get(self.root_url + "/randoms") + assert resp.json()['number'] <= 80 + + @capture("tests/test_random_string.db", recordMode="once") + def test_random_string(self): + resp = requests.get(self.root_url + "/randoms") + assert len(resp.json()['string']) <= 80 + + @capture("tests/test_hashname.db", recordMode="once") + def test_hashname(self): + req_body = { + "name": "Frootloops Johnson" + } + resp = requests.post(self.root_url + "/hashname", json=req_body) + assert resp.json()['name'] == "Frootloops Johnson" diff --git a/tests/test_simple.py b/tests/test_simple.py index 9787f5b..d32c532 100644 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -1,11 +1,10 @@ import json -from app import simple - +from app.simple import app class TestSimple: def setup(self): - self.app = simple.app.test_client() + self.app = app.test_client() def teardown(self): pass