diff --git a/openerp/addons/base/tests/test_acl.py b/openerp/addons/base/tests/test_acl.py
index 366a17129785591bc81dc3a8d9733dd6d3961776..9f6be133947e31c9504d405c614849b8da096d6e 100644
--- a/openerp/addons/base/tests/test_acl.py
+++ b/openerp/addons/base/tests/test_acl.py
@@ -111,12 +111,10 @@ class TestACL(common.TransactionCase):
         # accessing fields must no raise exceptions...
         part.name
         # ... 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'):
                 part.email
 
-        self.assertEqual(cm.exception.args[0], 'AccessError')
-
 if __name__ == '__main__':
     unittest2.main()
 
diff --git a/openerp/addons/test_new_api/tests/test_new_fields.py b/openerp/addons/test_new_api/tests/test_new_fields.py
index 74f85fa6d684283c7fcbdfdb6c912680c4431fe0..126a967ba484a2c7604d6a85639f78ad9509a102 100644
--- a/openerp/addons/test_new_api/tests/test_new_fields.py
+++ b/openerp/addons/test_new_api/tests/test_new_fields.py
@@ -185,6 +185,9 @@ class TestNewFields(common.TransactionCase):
         with self.assertRaises(Exception):
             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
         # messages in discussion
         discussion.participants += self.env.user
diff --git a/openerp/api.py b/openerp/api.py
index da9e62f1549c7df9475df31e6f885d7806ac83d6..94ed98c12658199b0e5e6321b2e9475fd9fb12fe 100644
--- a/openerp/api.py
+++ b/openerp/api.py
@@ -815,6 +815,24 @@ class Environment(object):
             env.computed.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):
         """ Check whether `field` must be recomputed, and returns a recordset
             with all records to recompute for `field`.
diff --git a/openerp/tests/common.py b/openerp/tests/common.py
index b3f6dbe81a366b7196a15f2c67c7e7324006c956..db84855b36a703043c3b16a643a33d2a2493fa61 100644
--- a/openerp/tests/common.py
+++ b/openerp/tests/common.py
@@ -16,6 +16,7 @@ import time
 import unittest2
 import urllib2
 import xmlrpclib
+from contextlib import contextmanager
 from datetime import datetime, timedelta
 
 import werkzeug
@@ -104,6 +105,20 @@ class BaseCase(unittest2.TestCase):
         module, xid = xid.split('.')
         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):
     """ 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, {})
 
     def tearDown(self):
+        # rollback and close the cursor, and reset the environments
+        self.env.reset()
         self.cr.rollback()
         self.cr.close()
 
@@ -139,9 +156,12 @@ class SingleTransactionCase(BaseCase):
 
     @classmethod
     def tearDownClass(cls):
+        # rollback and close the cursor, and reset the environments
+        cls.env.reset()
         cls.cr.rollback()
         cls.cr.close()
 
+
 class RedirectHandler(urllib2.HTTPRedirectHandler):
     """
     HTTPRedirectHandler is predicated upon HTTPErrorProcessor being used and