I've been successfully using pytest for quite a while now to do unit
testing for my web2py apps. Today, after upgrading to the latest web2py
version (Version 2.10.4-stable+timestamp.2015.04.26.15.11.54) my tests
won't run. When I try to run the tests in a test file (in the tests/
directory) web2py tries to import that test file as a module and raises an
ImportError.
For example, I have a test file at
myapp/tests/modules/test_greek_parser.py. I try to run it like this:
python2.7 -m pytest -xvs applications/paideia/tests/modules/
test_greek_parser.py
The error looks like this:
================================================= test session starts
=====================================
platform linux2 -- Python 2.7.9 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 0 items / 1 errors
======================================================= ERRORS
============================================
______________________ ERROR collecting applications/paideia/tests/modules/
test_greek_parser.py ___________
/usr/local/lib/python2.7/dist-packages/py/_path/local.py:620: in pyimport
> __import__(modname)
gluon/custom_import.py:89: in custom_importer
> raise ImportError, 'Cannot import module %s'
% str(e)
E ImportError: Cannot import module
'applications.paideia.modules.test_greek_parser'
=============================================== 1 error in 0.01 seconds
===================================
It's not surprising that custom_importer can't find the module, since it
doesn't exist! It's looking for a file with my test file name, but looking
in the myapp/modules/ directory. I'm not even sure why gluon is trying to
import something with that file name at all.
Now, if this isn't strange enough already, this import error only happens
with some of my test files. Others (in the same tests/modules/ directory)
run without a hitch. But I can't for the life of me find any significant
difference between the two.
I'm attaching a couple of files here in case they help shed any light:
1. my conftest.py which is read by pytest before running the tests (this
seems to be fine; it sits in the parent 'tests' directory)
2. test_paideia_utils.py (which runs without any problem)
3. test_greek_parser.py (which raises the import error)
I realize that this may appear to be a pytest problem rather than a web2py
problem. But the error is happening because custom_importer is being called
somewhere, so at the least it looks like some recent update has introduced
an incompatibility with pytest. Again, these tests were all running fine
(with the same conftest.py)
Thanks again for any help. I'm really at a loss with this one!
--
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
---
You received this message because you are subscribed to the Google Groups
"web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.
#!/usr/bin/env python
'''
py.test configuration and fixtures file.
This file is lightly adapted from the one by viniciusban;
Part of the web2py.test model app (https://github.com/viniciusban/web2py.test)
This file
- Tells application it's running in a test environment.
- Creates a complete web2py environment, similar to web2py shell.
- Creates a WebClient instance to browse your application, similar to a real
web browser.
- Propagates some application data to test cases via fixtures, like baseurl
and automatic appname discovery.
To write to db in test:
web2py.db.table.insert(**data)
web2py.db.commit()
To run tests:
cd web2py (you must be in web2py root directory to run tests)
python web2py.py -a my_password --nogui &
py.test -x [-l] [-q|-v] -s applications/my_app_name/tests
'''
import os
import pytest
import sys
from pprint import pprint
sys.path.insert(0, '')
# allow imports from modules and site-packages
dirs = os.path.split(__file__)[0]
appname = dirs.split(os.path.sep)[-2]
modules_path = os.path.join('applications', appname, 'modules')
if modules_path not in sys.path:
sys.path.append(modules_path) # imports from app modules folder
if 'site-packages' not in sys.path:
sys.path.append('site-packages') # imports from site-packages
from gluon.shell import env
web2py_env = env(appname, import_models=True,
extra_request=dict(is_local=True))
@pytest.fixture(scope='module')
def baseurl(appname):
'''The base url to call your application.
Change you port number as necessary.
'''
return 'http://localhost:8000/%s' % appname
@pytest.fixture(scope='module')
def appname():
'''Discover application name.
Your test scripts must be on applications/<your_app>/tests
'''
dirs = os.path.split(__file__)[0]
appname = dirs.split(os.path.sep)[-2]
return appname
@pytest.fixture(scope='module', autouse=True)
def fixture_create_testfile_for_application(request, appname):
'''
Creates a temp file to tell application she's running under a
test environment.
Usually you will want to create your database in memory to speed up
your tests and not change your development database.
This fixture is automatically run by py.test at module level. So, there's
no overhad to test performance.
'''
import os
import shutil
# note: if you use Ubuntu, you can allocate your test database on ramdisk
# simply using the /dev/shm directory.
temp_dir = '/dev/shm/' + appname
if not os.path.isdir(temp_dir):
os.mkdir(temp_dir)
# IMPORTANT: temp_filename must be the same as set in app/models/db.py
temp_filename = '%s/tests_%s.tmp' % (temp_dir, appname)
with open(temp_filename, 'w+') as tempfile:
tempfile.write('application %s running in test mode' % appname)
def _remove_temp_file_after_tests():
shutil.rmtree(temp_dir)
request.addfinalizer(_remove_temp_file_after_tests)
@pytest.fixture(autouse=True)
def fixture_cleanup_db(web2py):
'''Truncate all database tables before every single test case.
This can really slow down your tests. So, keep your test data small and try
to allocate your database in memory.
Automatically called by test.py due to decorator.
'''
# TODO: figure out db rollback to standard state (not truncate to None)
# web2py.db.rollback()
# for tab in web2py.db.tables:
# web2py.db[tab].truncate()
# web2py.db.commit()
print('ran fixture cleanup !!!!!!!!!!!!!!!!!!!!!!!!!')
pass
@pytest.fixture(scope='module')
def client(baseurl, fixture_create_testfile_for_application):
'''Create a new WebClient instance once per module.
'''
from gluon.contrib.webclient import WebClient
webclient = WebClient(baseurl)
return webclient
@pytest.fixture(scope='module')
def user_login(request, web2py, client, db):
"""
Provide a new, registered, and logged-in user account for testing.
"""
# register test user
auth = web2py.current.auth
reg_data = {'first_name': 'Homer',
'last_name': 'Simpson',
'email': '[email protected]',
'password': 'testing',
'time_zone': 'America/Toronto'}
# create new test user if necessary and delete if there's more than one
user_query = db((db.auth_user.first_name == reg_data['first_name']) &
(db.auth_user.last_name == reg_data['last_name']) &
(db.auth_user.email == reg_data['email']) &
(db.auth_user.time_zone == reg_data['time_zone']))
user_count = user_query.count()
if user_count == 0:
auth.table_user().insert(**reg_data)
elif user_count > 1:
u_count = user_count
userset = user_query.select()
while u_count > 1:
lastu = userset.last()
lastu.delete_record()
u_count -= 1
else:
pass
assert user_query.count() == 1
user_record = user_query.select().first()
assert user_record
# log test user in
auth.login_user(user_record)
assert auth.is_logged_in()
def fin():
"""
Delete the test user's account.
"""
user_record.delete_record()
# TODO: remove test user's performance record
assert user_query.count() == 0
request.addfinalizer(fin)
return user_record.as_dict()
@pytest.fixture(scope='module')
def web2py(appname, fixture_create_testfile_for_application):
'''
Create a Web2py environment similar to that achieved by Web2py shell.
It allows you to use global Web2py objects like db, request, response,
session, etc.
Concerning tests, it is usually used to check if your database is an
expected state, avoiding creating controllers and functions to help
tests.
'''
from gluon.shell import env
from gluon.storage import Storage
web2py_env = env(appname, import_models=True,
extra_request=dict(is_local=True))
# Uncomment next 3 lines to allow using global Web2py objects directly
# in your test scripts.
if hasattr(web2py_env, '__file__'):
del web2py_env['__file__'] # avoid py.test import error
globals().update(web2py_env)
return Storage(web2py_env)
@pytest.fixture(scope='module')
def db(web2py, request):
"""
Provides a access to the production database from within test functions.
While the web2py object (providing global framework objects standard in a
web2py session) has a module-level scope, this db fixture has a function-
level scope. This means that any code below is run for each test function,
allowing for function-level db setup and teardown.
The fin teardown function expects a 'newrows' dictionary to be created in
the requesting test function. This dictionary should have db table names as
its keys and a list of newly-inserted row id numbers as the values.
"""
mydb = web2py.db
newrows = getattr(request.node.instance, 'newrows', None)
def fin():
"""
Delete any newly inserted rows in the test database.
"""
print('checking for inserted rows to remove**********')
pprint(newrows)
if newrows:
print('removing')
for tbl, rowids in newrows.iteritems():
mydb(mydb[tbl].id.belongs(rowids)).delete()
for i in rowids:
assert not mydb[mydb[tbl]](i)
request.addfinalizer(fin)
return mydb
#! /usr/bin/python
# -*- coding: UTF-8 -*-
"""
Unit tests for the paideia_utils module
Configuration and some fixtures
the file tests/conftest.py
run with py.test -xvs path/to/tests/dir
"""
import pytest
from paideia_utils import GreekNormalizer, check_regex
from pprint import pprint
class TestGreekNormalizer():
"""
"""
@pytest.mark.skipif(False, reason='just because')
@pytest.mark.parametrize('string_in,string_out',
[('ἄγαπὴ', u'ἀγαπη'), # handle multiple accents
('“ἀγαπη”', u'"ἀγαπη"'), # handle curly quotes
('‘ἀγαπη’', u"'ἀγαπη'"),
(u'ἀγάπη', u'ἀγαπη'), # handle unicode input
('τίνος', u'τίνος'), # words to be *kept* accented
('τί ἐστῖν', u'τί ἐστιν'),
('τίς', u'τίς'),
('τίνα', u'τίνα'),
('τίνας', u'τίνας'),
('τίνι', u'τίνι'),
('Τίνος', u'Τίνος'),
('Τί', u'Τί'),
('Τίς', u'Τίς'),
('Τίνα', u'Τίνα'),
('Τίνας', u'Τίνας'),
('Τίνι', u'Τίνι'),
('τίς', u'τίς'), # handle q-iota on windows
('ἀγάπη', u'ἀγαπη'), # alpha
('ἀγὰπη', u'ἀγαπη'),
('ἀγᾶπη', u'ἀγαπη'),
('ἄν', u'ἀν'), # alpha with smooth breathing
('ἂν', u'ἀν'),
('ἆν', u'ἀν'),
('ἅν', u'ἁν'), # alpha with rough breathing
('ἃν', u'ἁν'),
('ἇν', u'ἁν'),
('πᾷν', u'πᾳν'), # alpha with iota subscript
('πᾲν', u'πᾳν'),
('πᾴν', u'πᾳν'),
('ᾄν', u'ᾀν'), # alpha with iota subscript & smooth
('ᾂν', u'ᾀν'),
('ᾆν', u'ᾀν'),
('ᾅν', u'ᾁν'), # alpha with iota subscript & rough
('ᾃν', u'ᾁν'),
('ᾇν', u'ᾁν'),
('πέν', u'πεν'), # epsilon
('πὲν', u'πεν'),
('ἒν', u'ἐν'), # epsilon with smooth
('ἔν', u'ἐν'),
('ἕν', u'ἑν'), # epsilon with rough
('ἓν', u'ἑν'),
('πῆν', u'πην'), # eta
('πήν', u'πην'),
('πὴν', u'πην'),
('ἤν', u'ἠν'), # eta with smooth
('ἢν', u'ἠν'),
('ἦν', u'ἠν'),
('ἥν', u'ἡν'), # eta with rough
('ἣν', u'ἡν'),
('ἧν', u'ἡν'),
('πῇν', u'πῃν'), # eta with iota subscript
('πῄν', u'πῃν'),
('πῂν', u'πῃν'),
("ᾕν", u"ᾑν"), # eta with subsc and rough
("ᾓν", u"ᾑν"),
("ᾗν", u"ᾑν"),
("ᾔν", u"ᾐν"), # eta with subsc and smooth
("ᾒν", u"ᾐν"),
("ᾖν", u"ᾐν"),
("όν", u"ον"), # omicron
("ὸν", u"ον"),
("ὅν", u"ὁν"), # omicron with rough
("ὃν", u"ὁν"),
("ὄν", u"ὀν"), # omicron with smooth
("ὂν", u"ὀν"),
("ῖν", u"ιν"), # iota
("ϊν", u"ιν"),
("ίν", u"ιν"),
("ὶν", u"ιν"),
("ίν", u"ιν"),
("ἵν", u"ἱν"), # iota with rough
("ἳν", u"ἱν"),
("ἷν", u"ἱν"),
("ἴν", u"ἰν"), # iota with smooth
("ἲν", u"ἰν"),
("ἶν", u"ἰν"),
("ῦν", u"υν"), # upsilon
("ϋν", u"υν"),
("ύν", u"υν"),
("ὺν", u"υν"),
("ὕν", u"ὑν"), # upsilon with rough
("ὓν", u"ὑν"),
("ὗν", u"ὑν"),
("ὔν", u"ὐν"), # upsilon with smooth
("ὒν", u"ὐν"),
("ὖν", u"ὐν"),
("ῶν", u"ων"), # omega
("ών", u"ων"),
("ὼν", u"ων"),
("ὥν", u"ὡν"), # omega with rough
("ὣν", u"ὡν"),
("ὧν", u"ὡν"),
("ὤν", u"ὠν"), # omega with smooth
("ὢν", u"ὠν"),
("ὦν", u"ὠν"),
("ῷν", u"ῳν"), # omega with subsc
("ῴν", u"ῳν"),
("ῲν", u"ῳν"),
("ᾥν", u"ᾡν"), # omega with subsc and rough
("ᾣν", u"ᾡν"),
("ᾧν", u"ᾡν"),
("ᾤν", u"ᾠν"), # omega with subsc and smooth
("ᾢν", u"ᾠν"),
("ᾦν", u"ᾠν"),
("῾Ων", u"Ὡν"), # improperly formed rough with caps
("῾Ρν", u"Ῥν"),
("῾Υν", u"Ὑν"),
("῾Αν", u"Ἁν"),
("῾Ον", u"Ὁν"),
("῾Εν", u"Ἑν"),
("῾Ιν", u"Ἱν"),
("᾿Αν", u"Ἀν"), # improperly formed smooth with caps
("᾿Ον", u"Ὀν"),
("᾿Ων", u"Ὠν"),
("᾿Ιν", u"Ἰν"),
("᾿Εν", u"Ἐν"),
('Άπη', u'Απη'), # alpha caps
('Ὰπη', u'Απη'),
('Ἄπη', u'Ἀπη'),
('Ἂπη', u'Ἀπη'),
('Ἅπη', u'Ἁπη'),
('Ἃπη', u'Ἁπη'),
("Έν", u"Εν"), # epsilon caps
("Ὲν", u"Εν"),
("Ἕν", u"Ἑν"),
("Ἓν", u"Ἑν"),
("Ἔν", u"Ἐν"),
("Ἒν", u"Ἐν"),
("Ἥν", u"Ἡν"), # eta caps
("Ἣν", u"Ἡν"),
("Ἧν", u"Ἡν"),
("Ἤν", u"Ἠν"),
("Ἢν", u"Ἠν"),
("Ἦν", u"Ἠν"),
("Ήν", u"Ην"),
("Ὴν", u"Ην"),
("Ἵν", u"Ἱν"), # iota caps
("Ἳν", u"Ἱν"),
("Ἷν", u"Ἱν"),
("Ϊν", u"Ιν"),
("Ίν", u"Ιν"),
("Ὶν", u"Ιν"),
("Ίν", u"Ιν"),
("Ὅν", u"Ὁν"), # omicron caps
("Ὃν", u"Ὁν"),
("Όν", u"Ον"),
("Ὸν", u"Ον"),
("Ὕν", u"Ὑν"), # upsilon caps
("Ὓν", u"Ὑν"),
("Ὗν", u"Ὑν"),
("Ϋν", u"Υν"),
("Ύν", u"Υν"),
("Ὺν", u"Υν"),
("Ών", u"Ων"), # omega caps
("Ὼν", u"Ων"),
("Ὥν", u"Ὡν"),
("Ὣν", u"Ὡν"),
("Ὧν", u"Ὡν"),
])
def test_normalize_accents(self, string_in, string_out):
"""
Unit testing function for paideia_utils.normalize_accents()
"""
print 'string in', string_in
print 'expected string out', string_out
actual = GreekNormalizer().normalize_accents(string_in)
print 'actual string out', actual
assert actual == string_out
assert isinstance(actual, unicode)
@pytest.mark.skipif(False, reason='just because')
@pytest.mark.parametrize('string_in,string_out',
[('Aγaπη', u'Αγαπη'),
('deλτα', u'δελτα'),
('ZEΔ', u'ΖΕΔ'),
('ΔH', u'ΔΗ'),
('τivα', u'τινα'),
('TIΣ', u'ΤΙΣ'),
('kαππα', u'καππα'),
('KΑΠΠΑ', u'ΚΑΠΠΑ'),
('ΑN', u'ΑΝ'), # TODO: Why does ἘΝ fail here?
('ἀπo', u'ἀπο'),
('ἈΠO', u'ἈΠΟ'),
('ὡpα', u'ὡρα'),
('ὩPΑ', u'ὩΡΑ'),
('tε', u'τε'),
('ἐxω', u'ἐχω'),
('ἘXΩ', u'ἘΧΩ'),
('ἐγw', u'ἐγω')
])
def test_convert_latin_chars(self, string_in, string_out):
"""
Unit testing function for paideia_utils.sanitize_greek()
"""
print 'string in', string_in
print 'expected string out', string_out
actual = GreekNormalizer().convert_latin_chars(string_in)
print 'actual string out', actual
assert actual == string_out
assert isinstance(actual, unicode)
@pytest.mark.skipif(False, reason='just because')
@pytest.mark.parametrize('string_in,string_out',
[('Aγaπη Aγaπη ', u'Aγaπη Aγaπη'),
])
def test_strip_extra_spaces(self, string_in, string_out):
"""
Unit testing function for paideia_utils.sanitize_greek()
"""
print 'string in', string_in
print 'expected string out', string_out
actual = GreekNormalizer().strip_extra_spaces(string_in)
print 'actual string out', actual
assert actual == string_out
assert isinstance(actual, unicode)
@pytest.mark.skipif(False, reason='just because')
@pytest.mark.parametrize('string_in,string_out',
[('῾Αγaπὴ Aγaπη, τί ἐστιv “ἀγαπη.”',
u'Ἁγαπη Αγαπη, τί ἐστιν "ἀγαπη."'),
])
def test_normalize(self, string_in, string_out):
"""
Unit testing function for paideia_utils.sanitize_greek()
"""
print 'string in', string_in
print 'expected string out', string_out
actual = GreekNormalizer().normalize(string_in)
print 'actual string out', actual
assert actual == string_out
assert isinstance(actual, unicode)
@pytest.mark.skipif(False, reason='just because')
@pytest.mark.parametrize('regex,stringdict',
[('^(?P<a>ἀγαπη )?ἐγω ἐστιν(?(a)| ἀγαπη)\.$',
{'ἀγαπη ἐγω ἐστιν.': True,
'ἐγω ἐστιν ἀγαπη.': True,
'ἀγαπη ἐγω ἐστιν ἀγαπη.': False,
'ἐγω ἐστιν ἀγαπη': False
}
)
])
def test_check_regex(regex, stringdict):
"""
"""
actual = check_regex(regex, stringdict.keys())
pprint(actual)
assert actual == stringdict
#! /usr/bin/python
# -*- coding: UTF-8 -*-
"""
Unit tests for the modulename module
Configuration and some fixtures
the file tests/conftest.py
run with py.test -xvs path/to/tests/dir
"""
import pytest
from collections import OrderedDict
#import re
#from pprint import pprint
from greek_parser import NounPhrase, Noun, tokenize
class TestNoun():
"""
"""
@pytest.mark.skipif(False, reason='just because')
@pytest.mark.parametrize('nominal,string,expected',
[('ἀρτον', # nominal
'Τον ἀρτον ὁ ἀνηρ πωλει.', # string
([OrderedDict([('τον', None),
('ἀρτον', ['Noun']),
('ὁ', None),
('ἀνηρ', None),
('πωλει', None)])
],
[])
),
])
def test_find_article(self, nominal, string, expected):
"""
"""
tkns = tokenize(string)[0]
actual = Noun('ἀρτον|λογον', ['top']).match_string([tkns], [])
print actual
assert actual == expected
class TestNounPhrase():
"""
"""
@pytest.mark.skipif(False, reason='just because')
@pytest.mark.parametrize('nominal,string,expected',
[('ἀρτον', 'Τον ἀρτον ὁ ἀνηρ πωλει.', 'pass'),
])
def test_find_article(self, nominal, string, expected):
"""
"""
tkns = tokenize(string)[0]
actual = NounPhrase([tkns]).find_article()
'''
@pytest.mark.skipif(False, reason='just because')
@pytest.mark.parametrize('string,expected',
[('Τον ἀρτον ὁ ἀνηρ πωλει.', 'pass'),
('Ὁ ἀνηρ πωλει τον ἀρτον.', 'pass'),
('Πωλει τον ἀρτον ὁ ἀνηρ.', 'pass'),
('τον πωλει ὁ ἀρτον ἀνηρ.', 'fail'),
('ὁ ἀρτον πωλει τον ἀνηρ.', 'fail'),
('ὁ τον ἀρτον πωλει ἀνηρ.', 'fail'),
('ὁ πωλει τον ἀρτον ἀνηρ.', 'fail')
])
def test_main(string, expected):
"""
"""
pattern = Clause(None,
Subject(None, Noun(r'ἀνηρ'), 'def'),
Verb(r'πωλει|ἀγοραζει'),
DirObject(None, Noun(r'ἀρτον'), 'def'),
'top')
actual = main(string, pattern)
assert actual == expected
'''