#!/usr/bin/env python
import argparse
import collections
import unittest
import sys
def main():
parser = argparse.ArgumentParser(description='Django-like testrunner')
parser.add_argument('specific_test', metavar='', type=str, nargs='?', help='Specifc test')
parser.add_argument("--failfast", action="store_true")
parser.add_argument("--verbosity", type=int, default=1)
args = parser.parse_args()
loader = unittest.TestLoader()
all_tests = loader.discover('.', top_level_dir="./")
suite = unittest.TestSuite()
if args.specific_test:
def walk_tests(tests):
if isinstance(tests, collections.Iterable):
for item in tests:
walk_tests(item)
return
if tests.id().startswith(args.specific_test):
suite.addTest(tests)
elif not str(tests).startswith("test"):
sys.exit("Error in file %s" % tests)
walk_tests(all_tests)
else:
suite.addTests(all_tests)
unittest.TextTestRunner(verbosity=args.verbosity, failfast=args.failfast).run(suite)
if __name__ == "__main__":
main()
search
Showing posts with label django. Show all posts
Showing posts with label django. Show all posts
Sunday, March 11, 2018
python: Django-like testrunner
Django-like testrunner for non-django projects:
Thursday, March 9, 2017
List data samples for faker
faker is a very useful library for generating beautiful test data. However, I was not able to find a proper description with all providers. All samples (plus some useless data) can be printed and inspected with the following code:
The output is:
from faker import Factory as FakerFactory
faker = FakerFactory.create()
methods = [method for method in dir(faker) if callable(getattr(faker, method)) and not method.startswith("_")]
for item in methods:
try:
print u"- %s:" % item
print u" %s" % getattr(faker, item)()
except TypeError:
pass
- address:
25611 Senger Cove
Cadenmouth, HI 46932-8013
- am_pm:
AM
- boolean:
False
- bothify:
07 sb
- bs:
re-contextualize customized mindshare
- building_number:
86338
- catch_phrase:
Expanded uniform complexity
- century:
III
- chrome:
Mozilla/5.0 (Windows NT 6.0) AppleWebKit/5361 (KHTML, like Gecko) Chrome/14.0.879.0 Safari/5361
- city:
Sporershire
- city_prefix:
Lake
- city_suffix:
port
- color_name:
Indigo
- company:
Flatley Inc
- company_email:
kbaumbach@mckenzie.com
- company_suffix:
LLC
- country:
Syrian Arab Republic
- country_code:
FI
- credit_card_expire:
12/26
- credit_card_full:
Diners Club / Carte Blanche
Pierre Donnelly
36417005868777 04/26
CVC: 568
- credit_card_number:
6011478274887340
- credit_card_provider:
VISA 16 digit
- credit_card_security_code:
081
- currency_code:
RSD
- date:
1982-04-04
- date_time:
1999-05-19 00:50:24
- date_time_ad:
1864-03-03 15:27:13
- date_time_between:
2002-08-21 16:01:56
- date_time_between_dates:
2017-03-08 21:56:07
- date_time_this_century:
2016-01-14 03:30:51
- date_time_this_decade:
2014-08-17 00:29:28
- date_time_this_month:
2017-03-03 08:27:52
- date_time_this_year:
2017-01-10 12:08:41
- day_of_month:
04
- day_of_week:
Saturday
- domain_name:
altenwerth.com
- domain_word:
cronawalker
- ean:
3265654672776
- ean13:
4119002606106
- ean8:
13383411
- email:
columbus64@gmail.com
- file_extension:
png
- file_name:
aperiam.html
- firefox:
Mozilla/5.0 (Windows NT 6.1; it-IT; rv:1.9.2.20) Gecko/2012-07-23 16:34:01 Firefox/4.0
- first_name:
Tella
- first_name_female:
Oliva
- first_name_male:
Arba
- format:
- free_email:
milford.rempel@yahoo.com
- free_email_domain:
yahoo.com
- geo_coordinate:
-82.910152
- get_formatter:
- get_providers:
[<faker.providers.user_agent.Provider object at 0x7fcce4201410>, <faker.providers.ssn.en_US.Provider object at 0x7fcce4201450>, <faker.providers.python.Provider object at 0x7fcce42013d0>, <faker.providers.profile.Provider object at 0x7fcce4201390>, <faker.providers.phone_number.en_US.Provider object at 0x7fcce4201350>, <faker.providers.person.en_US.Provider object at 0x7fcce4201310>, <faker.providers.misc.Provider object at 0x7fcce4201250>, <faker.providers.lorem.la.Provider object at 0x7fcce42012d0>, <faker.providers.job.en_US.Provider object at 0x7fcce4201290>, <faker.providers.internet.en_US.Provider object at 0x7fcce4201210>, <faker.providers.file.Provider object at 0x7fcce42011d0>, <faker.providers.date_time.Provider object at 0x7fcce4201190>, <faker.providers.currency.Provider object at 0x7fcce4201150>, <faker.providers.credit_card.Provider object at 0x7fcce4201110>, <faker.providers.company.en_US.Provider object at 0x7fcce4201090>, <faker.providers.color.en_US.Provider object at 0x7fcce42010d0>, <faker.providers.barcode.Provider object at 0x7fcce4201050>, <faker.providers.address.en_US.Provider object at 0x7fcce41e9fd0>]
- hex_color:
#69d7c0
- image_url:
http://www.lorempixum.com/373/972
- internet_explorer:
Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.01; Trident/3.1)
- ipv4:
129.146.124.124
- ipv6:
ef5e:860d:c643:8233:0ff0:d40d:7739:9844
- iso8601:
2007-01-25T19:03:46
- job:
Dancer
- language_code:
de
- last_name:
Romaguera
- last_name_female:
Heidenreich
- last_name_male:
Bogan
- latitude:
62.9329675
- lexify:
UGTW
- linux_platform_token:
X11; Linux x86_64
- linux_processor:
x86_64
- locale:
ru_TO
- longitude:
-49.789527
- mac_address:
41:9e:ad:ca:2a:fe
- mac_platform_token:
Macintosh; PPC Mac OS X 10_5_2
- mac_processor:
Intel
- md5:
04c1004fa2b0de119d764c4ec41e567d
- military_apo:
PSC 0750, Box 3490
- military_dpo:
Unit 1372 Box 0808
- military_ship:
USNV
- military_state:
AE
- mime_type:
message/rfc822
- month:
11
- month_name:
February
- name:
Dorcas Wolff MD
- name_female:
Amy Tromp
- name_male:
Colvin Hoppe
- null_boolean:
False
- numerify:
597
- opera:
Opera/9.27.(Windows NT 6.2; it-IT) Presto/2.9.173 Version/11.00
- paragraph:
Ex est maiores et maiores id voluptatem velit iure. Ratione ad enim sed. Quidem ea vel esse quis molestiae quisquam tempora.
- paragraphs:
[u'Ipsum consequatur et et et et. Voluptatem qui totam veritatis. Quasi vitae autem quos magnam ducimus. Doloremque minima perferendis quibusdam mollitia doloremque. Et temporibus porro quia eveniet sunt.', u'Neque nihil cupiditate sunt. Velit ducimus sint qui eaque nam.', u'Sequi labore laudantium expedita iusto aspernatur quibusdam ipsa hic. Et delectus numquam et fugit. Et quo explicabo quia voluptatem molestias blanditiis omnis. Mollitia facilis molestiae sunt aut eius quia.']
- parse:
- password:
H$E4isuB__
- phone_number:
322-309-6514
- postalcode:
36510
- postalcode_plus4:
31785-2317
- postcode:
63240
- prefix:
Dr.
- prefix_female:
Miss
- prefix_male:
Mr.
- profile:
{'website': [u'http://feest.com/', u'http://runolfsson.info/', u'http://www.wiegand.com/', u'http://www.rennerzemlak.org/'], 'username': u'bogisich.forest', 'name': u'Lorenz Friesen III', 'blood_group': 'A+', 'residence': u'87593 Douglas Pike\nBeahanberg, NY 08971-4439', 'company': u'Ratke and Sons', 'address': u'3721 Rath Springs\nMillerbury, AK 65443-3366', 'birthdate': '1985-10-14', 'sex': 'M', 'job': 'Scientist, research (medical)', 'ssn': u'658-53-8410', 'current_location': (Decimal('-69.179215'), Decimal('73.080343')), 'mail': u'natalee03@yahoo.com'}
- provider:
- pybool:
False
- pydecimal:
93907633.3459
- pydict:
{u'non': u'Sint at et ut ut.', u'voluptas': datetime(2011, 6, 30, 13, 28, 24), u'in': u'Consequatur modi ut.', u'temporibus': u'nberge@gmail.com', u'quam': 5946, u'quia': -50185653292693.0, u'corporis': 44989.34539971, u'aspernatur': datetime(2001, 5, 8, 5, 0, 4), u'id': u'Voluptas et.', u'consequatur': u'Sint voluptatum quo.'}
- pyfloat:
5.427466
- pyint:
5831
- pyiterable:
[u'Praesentium atque.', 510, u'Voluptatem.', u'Aspernatur culpa.', -86872010066.1, Decimal('1.39045051271E+12'), u'Aut neque quod quis.', 774237663.8, u'Libero voluptates.']
- pylist:
[u'Enim voluptatem.', u'In ut unde aut est.', 3744, u'Tenetur harum nam.', u'Amet nam quam.', Decimal('-2.15222088569E+13'), 56014295154.367]
- pyset:
set([2144, u'Est voluptas rem.', datetime(1989, 2, 18, 3, 52, 13), u'Est nihil.', Decimal('-243.7196'), datetime(1983, 9, 28, 12, 52, 56), u'beverlee76@yahoo.com', Decimal('-3.04622164413E+12'), u'Natus ut atque.'])
- pystr:
Ex non explicabo.
- pystruct:
- pytuple:
- random_digit:
5
- random_digit_not_null:
1
- random_digit_not_null_or_empty:
- random_digit_or_empty:
9
- random_element:
b
- random_int:
3804
- random_letter:
x
- random_number:
981
- randomize_nb_elements:
11
- rgb_color:
63,125,116
- rgb_color_list:
- rgb_css_color:
rgb(21,8,176)
- safari:
Mozilla/5.0 (iPod; U; CPU iPhone OS 3_1 like Mac OS X; en-US) AppleWebKit/533.9.6 (KHTML, like Gecko) Version/3.0.5 Mobile/8B112 Safari/6533.9.6
- safe_color_name:
fuchsia
- safe_email:
irvin.little@example.net
- safe_hex_color:
#dd7700
- secondary_address:
Suite 378
- seed:
None
- sentence:
Consequuntur unde quo non voluptas minima voluptatibus.
- sentences:
[u'Sapiente quia incidunt porro dolorum sapiente.', u'Et quaerat ad nesciunt maiores.', u'Ut excepturi autem qui laboriosam.']
- set_formatter:
- sha1:
aab5c833e76265e48aa35b5b39ed798b2d21d971
- sha256:
d74cbb45b74394c6a95f55f27926db1bdfeaf42dbbfcb3fddff22911f0dd4202
- simple_profile:
{'username': u'cbuckridge', 'name': u'Britta Cartwright', 'birthdate': '1974-08-05', 'sex': 'M', 'address': u'123 Talia Island\nSouth Thursa, DC 68141', 'mail': u'vonda67@hotmail.com'}
- slug:
ut-ducimus-illum
- ssn:
142-04-9440
- state:
Vermont
- state_abbr:
VT
- street_address:
33334 Auer Via
- street_name:
Tayler Pines
- street_suffix:
Curve
- suffix:
II
- suffix_female:
DVM
- suffix_male:
V
- text:
Eius ut atque voluptatum deserunt similique aliquid. Rerum quo commodi cum eius.
- time:
14:45:00
- time_delta:
16412 days, 22:34:37
- timezone:
Asia/Saigon
- tld:
com
- unix_time:
330977642
- uri:
http://boyer.com/main/index.php
- uri_extension:
.html
- uri_page:
post
- uri_path:
tags/posts
- url:
http://www.hammes.com/
- user_agent:
Mozilla/5.0 (compatible; MSIE 7.0; Windows 98; Trident/4.0)
- user_name:
jenkins.layton
- uuid4:
8fef0d23-2010-44f1-873a-49767169db27
- windows_platform_token:
Windows 95
- word:
omnis
- words:
[u'neque', u'odit', u'dolorum']
- year:
1975
- zipcode:
77130
- zipcode_plus4:
71629-5918
Test that the correct template was used in a view function when testing with RequestFactory in Django
Django has a special assertion method for that purpose:
with self.assertTemplateUsed("agents/incoming_queue.html"):
response = incoming_queue(self.request)
Test context of the rendered template with RequestFactory in Django
When writing tests for the Django's view functions I prefer to use RequestFactory instead of the django test client. Tests that use RequestFactory are easier to understand and they are faster.
For example, you need to test a simple view function:
If you want to test the context of the rendered template, it is easy to do with django test client as it attaches it to the response. If you want to use RequestFactory for that purpose, there is no easy way to do it.
It would be comfortable to have something like assertTemplateContextIn(expected_context) method, that checks that expected_context was used during the template rendering.
The following code was inspired by assertTemplateUsed method:
We ofthen pass querysets instances to the context of the rendered template. The code above checks if 2 QuerySets are the same by comparing their models and ids of the elements.
TemplateContextTestCase class can be used to extend TestCase class:
def incoming_queue(request):
if request.method == "POST":
pass
else:
incoming_queue_list = UsageHistory.objects.filter(company=request.user.agent.company, queued=True)
context = {
"incoming_queue_list": incoming_queue_list,
"page": "queue",
"agent_available_url": reverse("agent_available")
}
return render(request, 'agents/incoming_queue.html', context)
from copy import copy
from django.db.models.query import QuerySet
from django.test.signals import template_rendered
from django.test.utils import ContextList
class TemplateContextTestCase(object):
def assertTemplateContextIn(self, expected_context=None):
"""
Asserts that the template was rendered with the context that includes
expected_context. Example:
with self.assertTemplateContextIn(expected_context):
vuew_function(self.request)
"""
return _AssertTemplateContext(self, expected_context)
class _AssertTemplateContext(object):
def __init__(self, test_case, expected_context):
self.test_case = test_case
self.expected_context = expected_context
self.context = ContextList()
def on_template_render(self, sender, signal, template, context, **kwargs):
self.context.append(copy(context))
def test(self):
missing = []
mismatched = []
for key in self.expected_context.keys():
if key in self.context:
if self.context[key] != self.expected_context[key]:
match = False
# Compare QuerySets
if isinstance(self.context[key], QuerySet) and isinstance(self.expected_context[key], QuerySet):
if self.context[key].model == self.expected_context[key].model:
if list(self.context[key].values_list("id", flat=True)) == \
list(self.expected_context[key].values_list("id", flat=True)):
match = True
if not match:
mismatched.append(key)
else:
missing.append(key)
return missing, mismatched
def __enter__(self):
template_rendered.connect(self.on_template_render)
return self
def __exit__(self, exc_type, exc_value, traceback):
template_rendered.disconnect(self.on_template_render)
if exc_type is not None:
return
missing, mismatched = self.test()
message = ""
if missing:
message = message + "Missing keys: " + ", ".join(x for x in missing) + "\n"
if mismatched:
for item in mismatched:
message = message + "Context key mismatch '%s': %s != %s \n" % (
item, self.expected_context[item], self.context[item]
)
if message:
self.test_case.fail(message)
from django.test import TestCase, RequestFactory
from yourapp.tests.helpers import TemplateContextTestCase
from django.core.urlresolvers import reverse
class TestIncomingQueue(TestCase, TemplateContextTestCase):
def test_context(self):
agent = AgentFactory()
self.request.user = agent.user
expected = {
"page": "queue",
"agent_available_url": reverse("agent_available"),
"incoming_queue_list": UsageHistory.objects.none()
}
with self.assertTemplateContextIn(expected):
incoming_queue(self.request)
Monday, February 20, 2017
Show raw SQL queries in PostgreSQL
If you want to inspest what queries exactly Django ORM sends to the PostgreSQL, you can do it with postgres logging.
The first step is to enable logging. Add
to the file:
Then you are able to see raw SQL queries with command:
log_statement = 'all'
sudo vim /etc/postgresql/9.4/main/postgresql.conf
sudo tail -f /var/log/postgresql/postgresql-9.4-main.log
Monday, October 3, 2016
Speeding up Django tests: Disable migrations during tests
Running all migrations before the testrun can take a lot of time. For some reason, they are very slow. Running 50 migrations could easily take more than 1 minute!
Fourtunately, disabling migrations is very easy. Just add the following code to the test's settings file:
Some of our migrations add data to the database and tests expect this data to be there. To fix that problem we will need to overwrite test runner.
Add to the settings file:
Then create test_runner.py in the project root with the following content:
After recreating test database directly from the models (what is really fast), test runner will execute create_groups() function which populates database with all necessary data.
# Disable migrations when running tests
MIGRATION_MODULES = {
app[app.rfind('.') + 1:]: 'your_app.migrations_not_used_in_tests'
for app in INSTALLED_APPS
}
TEST_RUNNER = 'your_app.test_runner.CustomDiscoverRunner'
from django.test.runner import DiscoverRunner
from myapp.management.commands._setup_functions import create_groups
class CustomDiscoverRunner(DiscoverRunner):
def setup_databases(self, **kwargs):
result = super(CustomDiscoverRunner, self).setup_databases(**kwargs)
create_groups()
return result
Tuesday, September 16, 2014
django: Утечка памяти
Честно говоря, это не утечка памяти, а ожидаемое поведение django (о котором, я думаю, не все знают). Дело в том, что django очищает кэш при работе с базой данных после того, как view-функция возвращает response. Мне приходилось писать функции, которые не возвращают такой объект (например таска для celery). В этом случае, кэш необходимо очищать вручную:
from django.db import reset_queries
reset_queries()
Tuesday, April 8, 2014
django: Show form validation error with AJAX and jQuery
I once had a situation when I submited form with AJAX and if there were validation errors I had to put them at the same place as form was normally submited.
Assume that we have edit_user_data method in the views.py. If edit_form is valid we do some really useful things. If it's not, we should show user form validation errors (btw, I use uni_forms in that example).
Here is our code:
error_message is a dictionary which contains all form errors. We use JSON so we could have access to the data like server_response.result[key]. Key is the name of the field.
Here is our javascript code:
#edit_form - id of the submited form, url - where we submit the data to. If AJAX request successfuly was proceed by server (it doesn't matter if user submited valid data or not. It means he does submited something) we now could show user if his form is valid or not. If it is not valid, we remove old errors from form (or we will have multiple errors under the fields, probably duplicates). Let also assume that our form contains 5 fields: first_name, last_name, gender, sex, phone. Here you can see nice trick how to check if a key is related to one of our fields:
We also want to show user only one error per field (If there are more, he will be able to see them next time):
To show error message above the field itself we need to find that field:
Luckly our key names are the same as field names :) And then attach a message before it:
Where error is description of error, for example, "This field is required". Done. Errors looks nice and native ")
Assume that we have edit_user_data method in the views.py. If edit_form is valid we do some really useful things. If it's not, we should show user form validation errors (btw, I use uni_forms in that example).
Here is our code:
def edit_user_data(request):
# ...
if edit_form.is_valid():
# ...
else:
error_message = dict([(key, [unicode(error) for error in value]) for key, value in edit_form.errors.items()])
return HttpResponse(simplejson.dumps({"result": error_message, }), mimetype='application/json')
error_message is a dictionary which contains all form errors. We use JSON so we could have access to the data like server_response.result[key]. Key is the name of the field.
Here is our javascript code:
$("#edit_form").ajaxSubmit({
url: save_url,
type: 'post',
success: function (server_response) {
// Inform about successfull save
if (server_response.result_success) {
// ...
}
// Remove form errors
$("#edit_form").find(".errorField").remove();
for (var key in server_response.result) {
// Check for proper key in dictionary
if (key in {first_name: 1, last_name: 1, gender: 1, sex: 1, phone: 1, }) {
// Get error message
error = server_response.result[key][0];
// Find related field
field = $("#edit_form").find("#div_id_" + key)
// Attach error message before it
field.before('<p class="errorField"><em></em>' + error + '</p>');
}
}
},
});
#edit_form - id of the submited form, url - where we submit the data to. If AJAX request successfuly was proceed by server (it doesn't matter if user submited valid data or not. It means he does submited something) we now could show user if his form is valid or not. If it is not valid, we remove old errors from form (or we will have multiple errors under the fields, probably duplicates). Let also assume that our form contains 5 fields: first_name, last_name, gender, sex, phone. Here you can see nice trick how to check if a key is related to one of our fields:
if (key in {first_name: 1, second_name: 1, gender: 1, sex: 1, phone: 1, }) {}
We also want to show user only one error per field (If there are more, he will be able to see them next time):
error = server_response.result[key][0];
To show error message above the field itself we need to find that field:
field = $("#edit_form").find("#div_id_" + key)
Luckly our key names are the same as field names :) And then attach a message before it:
field.before('' + error + '
');
Where error is description of error, for example, "This field is required". Done. Errors looks nice and native ")
Subscribe to:
Posts (Atom)