Skip to content

Commit

Permalink
Merge pull request #56 from basxsoftwareassociation/feature/testing-t…
Browse files Browse the repository at this point in the history
…oolkit

Feature/testing toolkit
  • Loading branch information
saemideluxe authored Jul 7, 2021
2 parents 8868089 + fd484ba commit 9cbcb49
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 63 deletions.
106 changes: 44 additions & 62 deletions bread/tests/helper.py
Original file line number Diff line number Diff line change
@@ -1,71 +1,53 @@
from collections import namedtuple

import mechanize
from django.contrib.auth.models import User
from django.test import Client
from hypothesis import given, settings
from hypothesis.extra.django import TestCase, from_model

from bread.utils.urls import reverse_model

FormField = namedtuple("FormField", ["name", "value", "element", "generators"])


def authenticated(func):
def wrapper(case, *args, **kwargs):
user, _ = User.objects.get_or_create(username="admin", is_superuser=True)
case.client.force_login(user)
return func(case, *args, **kwargs)

return wrapper


@authenticated
def generic_bread_test(case, model, **kwargs):
"""Creates a single object, calls browse, read and edit pages"""
p = model(**kwargs)
p.save()
resp = case.client.get(reverse_model(model, "browse"))
case.assertEqual(resp.status_code, 200)
resp = case.client.get(reverse_model(model, "read", kwargs={"pk": p.pk}))
case.assertEqual(resp.status_code, 200)
test_form_page(case, reverse_model(model, "edit", kwargs={"pk": p.pk}))


def test_form_page(case, url, fuzzy_count=10):

# find input-elements and create generators
# ignore hidden, readonly and unusable fields
# this function is likely the challenging part of the implementation
url = f"{case.live_server_url}{url}"
fields = extract_form(url)

# save all fields unmodified and check save method works correctly
initial_fieldvalues = {i.name: i.value for i in extract_form(url)}
case.client.post(url, initial_fieldvalues) # post without changes
saved_fieldvalues = {i.name: i.value for i in extract_form(url)}
assert initial_fieldvalues == saved_fieldvalues
return

# test mutations on all fields separately in a loop
# more performant would be to modify many fields inside a single request
# but it would less fine-grained and not reflect how most real-world edits happen
for field in fields:
# field.generators returns a list of tuples with a generator and a boolean is_valid_value
# generator is a python generator which returns an infinite number of random values for the field
# is_valid_value determines whether the generator generates valid values for the field or not
for generator, is_valid_value in field.generators:
for testvalue, _ in zip(generator, range(fuzzy_count)):
case.client.post(url, {**initial_fieldvalues, field.name: testvalue})
updated_fields = case.client.get(url)

if is_valid_value:
assert updated_fields[field.name] == testvalue
else:
assert updated_fields[field.name] == initial_fieldvalues[field.name]


def extract_form(url):
br = mechanize.Browser()
br.open(url)
form = br.select_form(name="")
print(form)

return [FormField(name="", value="", element="", generators=[])]
def authenticate(client):
user, _ = User.objects.get_or_create(username="admin", is_superuser=True)
client.force_login(user)


def generic_bread_testcase(model, **kwargs):
class GenericModelTest(TestCase):
def setUp(self):
self.client = Client()
authenticate(self.client)

@given(from_model(model))
@settings(deadline=None)
def test_generic(self, instance):
self.assertIsNotNone(instance.pk)
resp = self.client.get(reverse_model(model, "browse"), follow=True)
self.assertEqual(resp.status_code, 200)
resp = self.client.get(
reverse_model(model, "read", kwargs={"pk": instance.pk}), follow=True
)
self.assertEqual(resp.status_code, 200)
resp = self.client.get(
reverse_model(model, "edit", kwargs={"pk": instance.pk}), follow=True
)
resp = self.client.get(
reverse_model(model, "delete", kwargs={"pk": instance.pk}), follow=True
)
self.assertEqual(resp.status_code, 200)

def test_add(self):
resp = self.client.get(reverse_model(model, "add"), follow=True)
self.assertEqual(resp.status_code, 200)
# TODO: implement add form, use hypothesis

@given(from_model(model))
@settings(deadline=None)
def test_forms(self, instance):
self.assertIsNotNone(instance.pk)
# TODO: implement edit form, use hypothesis

return GenericModelTest
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def readme():
"WeasyPrint", # creating PDFs
"django-ckeditor",
],
extras_require={"testing": ["mechanize"]},
extras_require={"testing": ["hypothesis[django]"]},
packages=find_packages(),
setup_requires=["setuptools_scm"],
zip_safe=False,
Expand Down

0 comments on commit 9cbcb49

Please sign in to comment.