0

I have the following class to be tested.

mod1/file1.py

@dataclass
class Base(metaclass=ABCMeta):

    account: Account
    ec2_id: str = ""

    def start(self):
        self.account.session.resource("ec2").instances.filter(
            InstanceIds=[self.ec2_id]
        ).start()

And I created the following test,

from unittest.mock import Mock, patch

    @patch('mod1.file1.Base')
    def test_start(sut):
        sut.account = Mock()
        sut.ec2_id = 'test'
        sut.start()  # sut.return_value.start() gets the same error
        sut.account.session.resource('ec2').instances.filter.assert_called_once()

However, the test failed with error

AssertionError: Expected 'filter' to have been called once. Called 0 times.
ca9163d9
  • 27,283
  • 64
  • 210
  • 413
  • You need `sut.return_value.start` as this is called by the instance. The question looks familiar, haven't you asked this before? And the same probably for the members, as this is a dataclass. – MrBean Bremen Feb 03 '22 at 16:49
  • It got the same error after I changed to `sut.return_value.start.start()`. – ca9163d9 Feb 03 '22 at 16:53
  • Ah ok, my bad - this won't work because you mock `Base`, so the original `start` will not be called. You probably have to mock `Account` instead, though that depends on how `Account` is instantiated. – MrBean Bremen Feb 03 '22 at 16:58
  • Actually you could just leave the code as is and instead of mocking `Base` just create a `Base` object as sut - that should work. Will put it in an answer for clarity... – MrBean Bremen Feb 03 '22 at 17:10

1 Answers1

2

The problem with the code is that you are mocking the object that you want to test. You have to mock any objects instantiated by your sut that you don't want to get created (because of unwanted side effect or complex initialization). If an object is mocked, it cannot be tested, so calling patched object sut is most certainly wrong.

In your case you need to instantiate the real system under test, e.g. the dataclass, and only mock Account (as you do), so something like this should work:

    def test_start():
        sut = Base()
        sut.account = Mock()
        sut.start()
        sut.account.session.resource('ec2').instances.filter.assert_called_once()

Note that setting sut.ec2_id has no impact, because filter is mocked and the arguments don't matter.

If this does really test the functionality you need to test is another matter.

MrBean Bremen
  • 14,916
  • 3
  • 26
  • 46