"""Testing utilities."""
from sqlalchemy import event
from sqlalchemy.engine import Engine
from .db import databases
[docs]class CommitException(BaseException):
"""Raised when a commit happens during testing."""
[docs]class Transact:
"""Helper context manager for handling transactions during tests. Perfect
for testing a single unit of work.
For testing sqlalchemy apps the common pattern is to usually, start
a transaction or savepoint, run the tests and once the test is done
rollback. Additionally it also prevents commits to the database so
that every test gets the same database state.
"""
@staticmethod
def _commit(connection, *args, **kwargs):
raise CommitException("Commits are not allowed")
[docs] def start(self):
"""Starts transaction management and hooks commit events."""
self.hook()
[docs] def stop(self):
"""Stops transaction management, rolls back transactions and unhooks
commit events."""
self.end()
self.unhook()
[docs] def hook(self):
"""Hooks commit events to prevent commits."""
if not event.contains(Engine, "commit", self._commit):
event.listen(Engine, "commit", self._commit)
if not event.contains(Engine, "commit_twophase", self._commit):
event.listen(Engine, "commit_twophase", self._commit)
[docs] def unhook(self):
"""Unhooks commit events."""
if event.contains(Engine, "commit", self._commit):
event.remove(Engine, "commit", self._commit)
if event.contains(Engine, "commit_twophase", self._commit):
event.remove(Engine, "commit_twophase", self._commit)
[docs] def end(self):
"""Rolls back transaction and removes session."""
databases.rollback()
databases.remove()
def __enter__(self):
self.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.stop()