Search code examples
pythonsqlalchemypython-unittest

Is there a way to mock a complete bit of code in pytest using mock?


For instance, every time a test finds

database.db.session.using_bind("reader")

I want to remove the using_bind("reader")) and just work with

database.db.session

using mocker

Tried to use it like this in conftest.py

@pytest.fixture(scope='function')
def session(mocker):
    mocker.patch('store.database.db.session.using_bind', return_value=_db.db.session)

But nothing has worked so far.

Code under test:

from store import database     
results = database.db.session.using_bind("reader").query(database.Order.id).join(database.Shop).filter(database.Shop.deleted == False).all(), 

and I get

AttributeError: 'scoped_session' object has no attribute 'using_bind' as an error.

Solution

  • Let's start with an MRE where the code under test uses a fake database:

    from unittest.mock import Mock, patch
    
    class Session:
        def using_bind(self, bind):
            raise NotImplementedError(f"Can't bind {bind}")
    
        def query(self):
            return "success!"
    
    
    database = Mock()
    database.db.session = Session()
    
    
    def code_under_test():
        return database.db.session.using_bind("reader").query()
    
    
    def test():
        assert code_under_test() == "success!"
    

    Running this test fails with:

    E       NotImplementedError: Can't bind reader
    

    So we want to mock session.using_bind in code_under_test so that it returns session -- that will make our test pass.

    We do that using patch, like so:

    @patch("test.database.db.session.using_bind")
    def test(mock_bind):
        mock_bind.return_value = database.db.session
        assert code_under_test() == "success!"
    

    Note that my code is in a file called test.py, so my patch call applies to the test module -- you will need to adjust this to point to the module under test in your own code.

    Note also that I need to set up my mock before calling the code under test.