1

I have a class SQLES in a separate script, sqles.py, which is imported in main.py and comprises methods to read SQL data and set the results in the class instance.

I am now writing a test script, test_main.py, in which I want to define a mock SQLES class to be used transparently by main.py instead of the real SQLES. I am not testing SQLES itself, and the idea of the mock class is to include the same set of methods but return hard-coded lists of data rows.

I have searched for hours, but not one result on python/mock/etc explains how to set up this apparently very simple requirement. This is the test script (truncated for clarity) which I have prepared so far:

import unittest.mock

import main    # main.py script I wish to run with the mocked SQLES class

class MockSQLES: # Dummy class I wish main.py to instantiate and use instead of SQLES

    def __init__(self):
        return

    def init(self, logger, cmd_args_record, cmd_args_playback):
        return

    # Couple of specimen methods, to give an idea of what dummy SQL class does ..

    def get_exchange_rates(self):
        self.exchange_rates['GBP'] = [
            [ '2021-09-13', 0.722298 ],
            [ '2021-08-20', 0.734203 ]
        ]
        return

    def get_firms_names_other(self):
         self.firms_names_other[firm_id] = [
            [ 131, 'Acme Industries' ],
            [ 132, 'Acme Research'   ]
        ]
        return

           ::::

with unittest.mock.patch('main.SQLES') as MockSQLES:

    main.main()

But when I try running it, a debug print after the instance assigment:

   sql = SQLES()

shows that the resulting sql object has type unittest.mock.MagicMock rather than the MockSQLES I expected, and sure enough the methods in the latter are not being called.

So in summary, I would like to know how to fix this. Perhaps Mock is the wrong approach, and some other method (monkey patching? ) is better suited for this.

Also, I don't wish to tinker with the main.py script any more than can be avoided. (It's obvious how a mock SQL class could be used if main.py had a test flag or similar to create an instance of either SQLES or MockSQLES.)

John R Ramsden
  • 355
  • 4
  • 14

1 Answers1

1

You are not using the mock class. When you performed this:

with unittest.mock.patch('main.SQLES') as MockSQLES:

The yielded response of the context manager unittest.mock.patch would be assigned to the as clause variable MockSQLES. So MockSQLES here isn't actually the mock class but is the yielded response of the patch. What you could try to do is:

with unittest.mock.patch('main.SQLES', MockSQLES) as mock_sql:

For reference of unittest.mock.patch, the 1st argument is the functionality to patch SQLES while the 2nd argument is the replacement MockSQLES as documented:

unittest.mock.patch(target, new=DEFAULT, ...)

... the target is patched with a new object

Make sure to also read about Mocking Classes for further reference.