Mocking Session
The mocking of the previous file query is working fine. But it’s necessary to check that the data is also saved in the database.
The project uses SQLAlchemy as an ORM to access the database.
To access the database with SQLAlchemy, a Session
instance is needed.
A Session
handles the connection to the database and database transactions.
With a Session
instance, it’s possible to perform database queries by calling query()
.
Transactions can be committed by calling commit()
.
Once commit is called, all data is written to the database.
To verify that data is saved to the database, it must be asserted that session.commit()
was called once.
The other option would be to query the database and check if the data was stored correctly.
But it’s not necessary to check if SQLAlchemy persisted the data correctly in the database.
It’s doubtful that SQLAlchemy is not working correctly.
So it just has to be verified that session.commit()
was called once.
For this, the Session
object can be mocked, so a call to commit()
can be quickly asserted.
Furthermore, nothing is saved in the database, and the test is independent from the database.
The Session
object is constructed in create_maya_scene
:
[...]
session = companyname_db.base.Session.object_session(parent)
[...]
Constructing objects for external dependencies in code that contains logic is an antipattern.
It’s possible to mock Session
using mock.patch, but this is not a good idea.
See this section on Dependency Injection.
It would be way better to pass a Session
to the function as a parameter.
But adding a parameter can break the whole code, and it’s not a good idea to touch code outside create_maya_scene
because no tests exist for this code.
But there is a trick: When a Session
keyword argument is added, the default value for the keyword argument can be None.
If no value is passed, the Session
can still be created inside create_maya_scene
, so no existing behavior changes.
In the test function, a mock can be passed as a Session
parameter.
This is safe to do because it’s only adding a parameter and one simple if statement.
This is pretty safe to do and ok to make the function testable:
def create_maya_scene(parent, name='main', uuid=None, user=None, filetype=None, description='', version=None,
current=None, variant='', session=None, **kwargs):
[...]
if not session:
session = companyname_db.base.Session.object_session(parent)
[...]
In the test function a mock is passed and the call to commit()
is asserted.
@mock.patch("[...]maya.api.filemanager.query_previous_files")
def test_create_maya_scene(mock_query_previous_files):
shot = Shot('myAwesomeShot')
user = User('Jan Honsbrok', 'janhon')
current = File(shot=shot)
mock_query_previous_files.return_value = [File(shot=shot, name='test', filetype='filetype', version=0)]
mock_session = mock.MagicMock()
create_maya_scene(shot, user=user, current=current, session=mock_session)
mock_session.commit.assert_called_once()