readme and refactoring
This commit is contained in:
parent
ad592ce845
commit
c0523b2905
29
README.md
29
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.
|
@ -1,4 +1,3 @@
|
|||||||
import threading
|
|
||||||
from random import randint
|
from random import randint
|
||||||
from secrets import choice
|
from secrets import choice
|
||||||
from string import ascii_letters
|
from string import ascii_letters
|
||||||
|
53
install.sh
Executable file
53
install.sh
Executable 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
3
rundemo.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
pipenv run python -m pytest tests/test_hov.py --verbose --show-capture=all
|
@ -6,21 +6,39 @@ from hoverpy import capture
|
|||||||
|
|
||||||
class TestUsingHoverpy:
|
class TestUsingHoverpy:
|
||||||
|
|
||||||
|
root_url = "http://127.0.0.1:5000"
|
||||||
|
|
||||||
def setup_class(self):
|
def setup_class(self):
|
||||||
# startup the simple server (only needed for this example)
|
# 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)
|
Popen("sh pipenv run python app/simple.py", shell=True, stdout=PIPE, stderr=PIPE)
|
||||||
|
|
||||||
def teardown_class(self):
|
def teardown_class(self):
|
||||||
# deletes the captured session
|
# deletes the captured sessions
|
||||||
os.remove("tests/test_version_call.db")
|
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")
|
@capture("tests/test_version_call.db", recordMode="once")
|
||||||
def test_version_call(self):
|
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
|
assert resp.json()['version'] == 0.1
|
||||||
|
|
||||||
# FROM THE DEMO:
|
@capture("tests/test_random_numbers.db", recordMode="once")
|
||||||
# @capture("tests/test_time2.db", recordMode="once")
|
def test_random_numbers(self):
|
||||||
# def test_time3(self):
|
resp = requests.get(self.root_url + "/randoms")
|
||||||
# time = requests.get("http://time.jsontest.com")
|
assert resp.json()['number'] <= 80
|
||||||
# assert list(time.json().keys()).index('time') > 0
|
|
||||||
|
@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"
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import json
|
import json
|
||||||
from app import simple
|
from app.simple import app
|
||||||
|
|
||||||
|
|
||||||
class TestSimple:
|
class TestSimple:
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.app = simple.app.test_client()
|
self.app = app.test_client()
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
pass
|
pass
|
||||||
|
Loading…
Reference in New Issue
Block a user