user2430771 user2430771 - 1 month ago 11
Python Question

Using python mocking library on sqlalchemy

I'm using sqlalchemy to query my databases for a project. Side-by-side, I'm new to unit testing and I'm trying to learn how can I make unit tests to test my database. I tried to use mocking library to test but so far, it seems to be very difficult.

So I made a piece of code which creates a

Session
object. This object is used to connect to database.

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.exc import OperationalError, ArgumentError

test_db_string = 'postgresql+psycopg2://testdb:hello@localhost/' \
'test_databasetable'
def get_session(database_connection_string):
try:
Base = declarative_base()
engine = create_engine(database_connection_string)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
connection = session.connection()
return session
except OperationalError:
return None
except ArgumentError:
return None


So I made a unit test case for this function:

import mock
import unittest
from mock import sentinel
import get_session

class TestUtilMock(unittest.TestCase):

@mock.patch('app.Utilities.util.create_engine') # mention the whole path
@mock.patch('app.Utilities.util.sessionmaker')
@mock.patch('app.Utilities.util.declarative_base')
def test_get_session1(self, mock_delarative_base, mock_sessionmaker,
mock_create_engine):
mock_create_engine.return_value = sentinel.engine
get_session('any_path')
mock_delarative_base.called
mock_create_engine.assert_called_once_with('any_path')
mock_sessionmaker.assert_called_once_with(bind=sentinel.engine)


As you see in my unit test, I cannot test code in
get_session()
starting from line
session = DBSession()
. So far after googling, I cannot find out if mock value returned can be also used to mock function calls - something like, I mock
session
object and verify if I called
session.connection()


Is the above method of writing unit test case the right way? Is there a better method to do this?

Answer

First of all you can test session = DBSession() line also by

self.assertEqual(get_session('any_path'), mock_sessionmaker.return_value.return_value)

Morover mock_delarative_base.called is not an assert and cannot fails. Replace it by

self.assertTrue(mock_delarative_base.called)

General considerations

Write test like these can be very time consuming and make your production code really coupled to test code. It is better to write your own wrapper that present a comfortable interface for your business code and test it by some mocks and real (simple) tests: you should trust sqlalchemy implementation and just test how your wrapper call it

After you can either mock your wrapper by a fake object that you can control or use mocks and check how your code call it but your wrapper will present just some business interface and mock it should be very simple.