[FIX] tests: make sure that a failed tests does not leave the environment dirty

When a failure occurs, or when exiting an assertRaises(), the environment
should not contain fields to recompute.
This commit is contained in:
Raphael Collet 2014-11-13 17:40:41 +01:00
parent 2052c16d21
commit 908252ec88
4 changed files with 42 additions and 3 deletions

View File

@ -111,12 +111,10 @@ class TestACL(common.TransactionCase):
# accessing fields must no raise exceptions... # accessing fields must no raise exceptions...
part.name part.name
# ... except if they are restricted # ... except if they are restricted
with self.assertRaises(openerp.osv.orm.except_orm) as cm: with self.assertRaises(openerp.exceptions.AccessError):
with mute_logger('openerp.models'): with mute_logger('openerp.models'):
part.email part.email
self.assertEqual(cm.exception.args[0], 'AccessError')
if __name__ == '__main__': if __name__ == '__main__':
unittest2.main() unittest2.main()

View File

@ -185,6 +185,9 @@ class TestNewFields(common.TransactionCase):
with self.assertRaises(Exception): with self.assertRaises(Exception):
self.env['test_new_api.message'].create({'discussion': discussion.id, 'body': 'Whatever'}) self.env['test_new_api.message'].create({'discussion': discussion.id, 'body': 'Whatever'})
# make sure that assertRaises() does not leave fields to recompute
self.assertFalse(self.env.has_todo())
# put back oneself into discussion participants: now we can create # put back oneself into discussion participants: now we can create
# messages in discussion # messages in discussion
discussion.participants += self.env.user discussion.participants += self.env.user

View File

@ -815,6 +815,24 @@ class Environment(object):
env.computed.clear() env.computed.clear()
env.dirty.clear() env.dirty.clear()
def clear(self):
""" Clear all record caches, and discard all fields to recompute.
This may be useful when recovering from a failed ORM operation.
"""
self.invalidate_all()
self.all.todo.clear()
@contextmanager
def clear_upon_failure(self):
""" Context manager that clears the environments (caches and fields to
recompute) upon exception.
"""
try:
yield
except Exception:
self.clear()
raise
def field_todo(self, field): def field_todo(self, field):
""" Check whether `field` must be recomputed, and returns a recordset """ Check whether `field` must be recomputed, and returns a recordset
with all records to recompute for `field`. with all records to recompute for `field`.

View File

@ -16,6 +16,7 @@ import time
import unittest2 import unittest2
import urllib2 import urllib2
import xmlrpclib import xmlrpclib
from contextlib import contextmanager
from datetime import datetime, timedelta from datetime import datetime, timedelta
import werkzeug import werkzeug
@ -104,6 +105,20 @@ class BaseCase(unittest2.TestCase):
module, xid = xid.split('.') module, xid = xid.split('.')
return self.registry('ir.model.data').get_object(self.cr, self.uid, module, xid) return self.registry('ir.model.data').get_object(self.cr, self.uid, module, xid)
@contextmanager
def _assertRaises(self, exception):
""" Context manager that clears the environment upon failure. """
with super(BaseCase, self).assertRaises(exception):
with self.env.clear_upon_failure():
yield
def assertRaises(self, exception, func=None, *args, **kwargs):
if func:
with self._assertRaises(exception):
func(*args, **kwargs)
else:
return self._assertRaises(exception)
class TransactionCase(BaseCase): class TransactionCase(BaseCase):
""" TestCase in which each test method is run in its own transaction, """ TestCase in which each test method is run in its own transaction,
@ -120,6 +135,8 @@ class TransactionCase(BaseCase):
self.env = api.Environment(self.cr, self.uid, {}) self.env = api.Environment(self.cr, self.uid, {})
def tearDown(self): def tearDown(self):
# rollback and close the cursor, and reset the environments
self.env.reset()
self.cr.rollback() self.cr.rollback()
self.cr.close() self.cr.close()
@ -139,9 +156,12 @@ class SingleTransactionCase(BaseCase):
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
# rollback and close the cursor, and reset the environments
cls.env.reset()
cls.cr.rollback() cls.cr.rollback()
cls.cr.close() cls.cr.close()
class RedirectHandler(urllib2.HTTPRedirectHandler): class RedirectHandler(urllib2.HTTPRedirectHandler):
""" """
HTTPRedirectHandler is predicated upon HTTPErrorProcessor being used and HTTPRedirectHandler is predicated upon HTTPErrorProcessor being used and