Added support for the Hitch integration testing framework.

This commit is contained in:
Colm O'Connor 2015-08-01 18:26:06 +08:00
parent 7d1bfef20b
commit 18b1843481
7 changed files with 295 additions and 1 deletions

View File

@ -152,8 +152,26 @@ The email server listens on 127.0.0.1:1025
It's time to write the code!!!
Running end to end integration tests
------------------------------------
To run the tests, enter the {{cookiecutter.repo_name}}/tests directory and run the following commands::
$ hitch init
Then run the stub test::
$ hitch test stub.test
This will download and compile python, postgres and redis and install all python requirements so the first time it runs it may take a while.
Subsequent test runs will be much quicker.
The testing framework runs Django, Celery (if enabled), Postgres, HitchSMTP (a mock SMTP server), Firefox/Selenium and Redis.
Deployment
------------
----------
It is possible to deploy to Heroku or to your own server by using Dokku, an open source Heroku clone.

View File

@ -22,3 +22,23 @@ libwebp-dev
##django-extensions
graphviz-dev
##hitch
python-setuptools
python3-dev
python-virtualenv
python-pip
firefox
automake
libtool
libreadline6
libreadline6-dev
libreadline-dev
libsqlite3-dev
libxml2
libxml2-dev
libssl-dev
libbz2-dev
wget
curl
llvm

View File

@ -0,0 +1,4 @@
{% raw %}{% for python_version in python_versions %}
{% block test scoped %}
{% endblock %}
{% endfor %}{% endraw %}

View File

@ -0,0 +1,157 @@
from subprocess import call
from os import path
import hitchpostgres
import hitchselenium
import hitchpython
import hitchserve
import hitchredis
import hitchtest
import hitchsmtp
# Get directory above this file
PROJECT_DIRECTORY = path.abspath(path.join(path.dirname(__file__), '..'))
class ExecutionEngine(hitchtest.ExecutionEngine):
"""Engine for orchestating and interacting with the app."""
def set_up(self):
"""Ensure virtualenv present, then run all services."""
python_package = hitchpython.PythonPackage(
python_version=self.preconditions['python_version']
)
python_package.build()
python_package.verify()
call([
python_package.pip, "install", "-r",
path.join(PROJECT_DIRECTORY, "requirements/local.txt")
])
postgres_package = hitchpostgres.PostgresPackage(
version=self.settings["postgres_version"],
)
postgres_package.build()
postgres_package.verify()
redis_package = hitchredis.RedisPackage(version="2.8.4")
redis_package.build()
redis_package.verify()
self.services = hitchserve.ServiceBundle(
project_directory=PROJECT_DIRECTORY,
startup_timeout=float(self.settings["startup_timeout"]),
shutdown_timeout=5.0,
)
postgres_user = hitchpostgres.PostgresUser("{{cookiecutter.repo_name}}", "password")
self.services['Postgres'] = hitchpostgres.PostgresService(
postgres_package=postgres_package,
users=[postgres_user, ],
databases=[hitchpostgres.PostgresDatabase("{{cookiecutter.repo_name}}", postgres_user), ]
)
self.services['HitchSMTP'] = hitchsmtp.HitchSMTPService(port=1025)
self.services['Django'] = hitchpython.DjangoService(
python=python_package.python,
port=8000,
version=str(self.settings.get("django_version")),
settings="config.settings.local",
needs=[self.services['Postgres'], ],
env_vars=self.settings['environment_variables'],
)
self.services['Redis'] = hitchredis.RedisService(
redis_package=redis_package,
port=16379,
)
{% if cookiecutter.celery_support == "y" %}
self.services['Celery'] = hitchpython.CeleryService(
python=python_package.python,
version="3.1.18",
app="{{cookiecutter.repo_name}}.taskapp", loglevel="INFO",
needs=[
self.services['Redis'], self.services['Django'],
],
env_vars=self.settings['environment_variables'],
)
{% endif %}
self.services['Firefox'] = hitchselenium.SeleniumService(
xvfb=self.settings.get("quiet", False)
)
# import hitchcron
# self.services['Cron'] = hitchcron.CronService(
# run=self.services['Django'].manage("trigger").command,
# every=1,
# needs=[ self.services['Django'], ],
# )
self.services.startup(interactive=False)
# Configure selenium driver
self.driver = self.services['Firefox'].driver
self.driver.set_window_size(self.settings['window_size']['height'], self.settings['window_size']['width'])
self.driver.set_window_position(0, 0)
self.driver.implicitly_wait(2.0)
self.driver.accept_next_alert = True
def pause(self, message=None):
"""Stop. IPython time."""
if hasattr(self, 'services'):
self.services.start_interactive_mode()
self.ipython(message)
if hasattr(self, 'services'):
self.services.stop_interactive_mode()
def load_website(self):
"""Navigate to website in Firefox."""
self.driver.get(self.services['Django'].url())
def click(self, on):
"""Click on HTML id."""
self.driver.find_element_by_id(on).click()
def fill_form(self, **kwargs):
"""Fill in a form with id=value."""
for element, text in kwargs.items():
self.driver.find_element_by_id(element).send_keys(text)
def click_submit(self):
"""Click on a submit button if it exists."""
self.driver.find_element_by_css_selector("button[type=\"submit\"]").click()
def confirm_emails_sent(self, number):
"""Count number of emails sent by app."""
assert len(self.services['HitchSMTP'].logs.json()) == int(number)
def wait_for_email(self, containing=None):
"""Wait for, and return email."""
self.services['HitchSMTP'].logs.out.tail.until_json(
lambda email: containing in email['payload'] or containing in email['subject'],
timeout=25,
lines_back=1,
)
def time_travel(self, days=""):
"""Make all services think that time has skipped forward."""
self.services.time_travel(days=int(days))
def on_failure(self):
"""Stop and IPython."""
if not self.settings['quiet']:
if self.settings.get("pause_on_failure", False):
self.pause(message=self.stacktrace.to_template())
def on_success(self):
"""Pause on success if enabled."""
if self.settings.get("pause_on_success", False):
self.pause(message="SUCCESS")
def tear_down(self):
"""Shut down services required to run your test."""
if hasattr(self, 'services'):
self.services.shutdown()

View File

@ -0,0 +1,26 @@
click==4.0
colorama==0.3.3
faketime==0.9.6.3
hitchcron==0.2
hitchpostgres==0.6.1
hitchpython==0.2
hitchredis==0.4.1
hitchselenium==0.3.1
hitchserve==0.4.1
hitchsmtp==0.2.1
hitchtest==0.6.8
humanize==0.5.1
ipython==3.1.0
Jinja2==2.7.3
MarkupSafe==0.23
patool==1.7
psutil==3.0.0
python-build==0.2.1
pyuv==1.0.2
PyYAML==3.11
requests==2.7.0
selenium==2.46.0
six==1.9.0
tblib==1.0.1
tornado==4.2
xeger==0.3

View File

@ -0,0 +1,59 @@
postgres_version: 9.3.9
redis_version: 2.8.4
django_version: 1.8.3
celery_version: 3.1.18
pause_on_success: false
pause_on_failure: true
startup_timeout: 45
environment_variables:
DATABASE_URL: postgres://{{cookiecutter.repo_name}}:password@127.0.0.1:15432/{{cookiecutter.repo_name}}
SECRET_KEY: cj5^uos4tfCdfghjkf5hq$9$(@-79^e9&x$3vyf#igvsfm4d=+
CELERY_BROKER_URL: redis://localhost:16379
window_size:
width: 450
height: 450
python_versions:
- 2.7.10
environment:
- approved_platforms:
- linux
- darwin
- freeports:
- 1025
- 8000
- 15432
- 16379
- brew:
- libtool
- automake
- node
- debs:
- python-setuptools
- python3-dev
- python-virtualenv
- python-pip
- firefox
- automake
- libtool
- libreadline6
- libreadline6-dev
- libreadline-dev
- libsqlite3-dev
- libpq-dev
- libxml2
- libxml2-dev
- libssl-dev
- libbz2-dev
- wget
- curl
- llvm
- graphviz-dev
- libtiff4-dev
- libjpeg8-dev
- libfreetype6-dev
- liblcms1-dev
- libwebp-dev
- zlib1g-dev
- gettext
- python-dev
- build-essential

View File

@ -0,0 +1,10 @@
{% raw %}{% extends "base.yml" %}
{% block test %}
- engine: engine.py:ExecutionEngine
name: Stub {{ python_version }}
preconditions:
python_version: "{{ python_version }}"
scenario:
- Load website
- Pause
{% endblock %}{% endraw %}