mercredi 4 novembre 2015

Testing SQLAlchemy Wrapping Class with Pytest

I have a (idea for) a class that I am really struggling how to test properly. In a perhaps futile exercise of test-driven development, I am trying to get the tests nailed down before the implementation, but here is a skeleton of the class:

class SQLiteStorageBackend(AStorageBackend): # inherits from an abstract class
    def __init__(self, storage_location):
        storage_path = os.path.abspath(storage_location)
        self._engine = create_engine('sqlite:///{}'.format(storage_location))
        self._Session = sessionmaker(bind=self._engine)

    def add_event(self, event):
        session = self._Session()
        session.add(event)
        session.commit()

    # *other methods similar*

As you can see, I am trying to abstract all the parts of my program that interact with the database in to this class. That way, I can just create instances of my models and pass them in.

I have been all over the SQLAlchemy docs and a variety of blog postings to see how testing is done on SQLAlchemy applications. The easy one to come up with was this fixture:

@pytest.fixture(scope='session')
def mock_sqlite_storage_path(tmpdir_factory):
    """Obtains a path for a mock SQLite database. Scoped to 'session' so the same file is used for each test."""
    tmp_path = tmpdir_factory.mktemp('db').join('test_db.sqlite').strpath
    storage_path = os.path.abspath(tmp_path)
    return storage_path

This allows me to create test instances like so:

def test_some_stuff(mock_sqlite_storage_path):
    testInstance = SQLiteStorageBackend(mock_sqlite_storage_path)
    testInstance.add_event(an_event_from_elsewhere)
    assert TheRightThingIsInTheDatabase # I know how to do this, just didn't want to write it out

The questions I can't figure out are thus:

  1. What kind of fixture do I create in order to rollback changes made during each test? The session is created and committed during the method call under test. Can some sort of nested transaction be used, or do I have to monkeypatch session.commit?
  2. When I am testing other functionality that interacts with the SQLiteStorageBackend, how should I mock it out?
  3. When do I need to call MetaData.create_all(), or is it even necessary? Where/when do models have to be imported? In the end, I would like to be able to create new models relatively easily without adding them to the SQLiteStorageBackend.

Aucun commentaire:

Enregistrer un commentaire