readme and refactoring

This commit is contained in:
Greg Gauthier 2019-05-23 13:19:43 +01:00
parent ad592ce845
commit c0523b2905
6 changed files with 112 additions and 11 deletions

View File

@ -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.

View File

@ -1,4 +1,3 @@
import threading
from random import randint
from secrets import choice
from string import ascii_letters

53
install.sh Executable file
View File

@ -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!"

3
rundemo.sh Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env sh
pipenv run python -m pytest tests/test_hov.py --verbose --show-capture=all

View File

@ -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"

View File

@ -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