Mocking a property call returning MagicMock, not value

10,688

It looks to me like it would be better to just replace the entire ConfigB object in the FileRunner namespace. Then your test looks something like this:

import unittest
import unittest.mock imort MagicMock
import mock
from FileRunner import FileRunner

class TestFileRunner(unittest.TestCase):
  @mock.patch('FileRunner.ConfigB')
  def test_methodscalled(self, cfgB):
    fileRunner = FileRunner()
    cfgB.return_value.Id = 17
    fileRunner.runProcess(1)

Note that @mock.patch('FileRunner.ConfigB') is going to replace the ConfigB class in the FileRunner namespace with a mock. You can then configure the mock to do whatever you like -- e.g. have an Id that equals 17.

Share:
10,688
EliSquared
Author by

EliSquared

Updated on June 14, 2022

Comments

  • EliSquared
    EliSquared almost 2 years

    I have the following configuration class:

    class ConfigB(object):
      Id = None
    
      def __Init__(self, Id):
        self.Id = Id
    

    Which is instantiated in the following class and the property printed:

    from config.ConfigB import ConfigB
    
    class FileRunner(object):
      def runProcess(self, Id)
        cfgB = ConfigB(Id)
        print(cfgB.Id)
    

    I have created the following test class to test it, where I am trying to mock both the instantiation and the cfgB.Id property call:

    import unittest
    import unittest.mock imort MagicMock
    import mock
    from FileRunner import FileRunner
    
    class TestFileRunner(unittest.TestCase):
      @mock.patch('ConfigB.ConfigB.__init__')
      @mock.patch('ConfigB.ConfigB.Id')
      def test_methodscalled(self, cfgBId, cfgBInit):
    
    
        fileRunner = FileRunner()
    
        cfgBId.return_value = 17
    
        cfgBInit.return_value = None
    
        print(cfgBId)
    
        fileRunner.runProcess(1)
    

    Note the print(cfgBId) statement before fileRunner is called. I get the following output:

    <MagicMock name='Id' id='157297352'>
    <MagicMock name='Id' id='157297352'>
    

    For some reason when I set the return value here in the test class:

    cfgBId.return_value = 17
    

    That is not getting called on the line in the FileRunner() class:

    print(cfgB.Id)
    

    What do I need to do to properly get my configuration property to display?

    Also note that my 'ConfigB' class instantiation is much more complicated than displayed above which is why I want to patch the instantiation and the call to the Id property.

    *Update: I have changed my class as suggested by @mgilson but it is still not working:

    import unittest
    import unittest.mock imort MagicMock
    import mock
    from FileRunner import FileRunner
    
    class TestFileRunner(unittest.TestCase):
      @mock.patch('FileRunner.ConfigB')
      def test_methodscalled(self, cfgB):
    
        fileRunner = FileRunner()
    
        cfgB.Id = 17
    
        print(cfgBId)
    
        fileRunner.runProcess(1)
    

    I am now getting the following output from the two print statements:

    <MagicMock name='ConfigB' id='157793640'>
    <MagicMock name='ConfigB().Id' id='157020512'>
    

    Any ideas why the above isn't working?

    *Solution:

    I was able to get it to work after a minor change to the test method that @mgilson suggested:

    import unittest
    from unittest.mock import MagicMock
    import mock
    from FileRunner import FileRunner
    
    class TestFileRunner(unittest.TestCase):
      @mock.patch('FileRunner.ConfigB')
      def test_methodscalled(self, configB):
    
        fileRunner = FileRunner()
    
        cfgB = MagicMock()
        cfgB.Id = 17
        #This will return the cfgB MagicMock when `ConfigB(Id)` is called in `FileRunner` class
        configB.return_value = cfgB 
    
        print(cfgB.Id)
    
        fileRunner.runProcess(1)
    
        #To test whether `ConfigB(17)` was called
        configB.assert_called_with(17)
    

    I now get the following outputs:

    <MagicMock name='ConfigB' id='157147936'>
    17
    
  • EliSquared
    EliSquared almost 7 years
    So I tried to implement that (see my edit on my post), but I am still not getting the correct outputs.
  • EliSquared
    EliSquared almost 7 years
    Acutally @mgilson I was able to get it to work I think I just changed the line in your code from cfgB.Id = 17 to cfgB().Id = 17. The parenthesis changed everything. Does that make sense to you? If you edit your post to include parenthesis I can mark it as the solution.
  • EliSquared
    EliSquared almost 7 years
    So I couldn't get your suggestion to work, but I did find a solution with the help of @mgilson. See my final update.
  • mgilson
    mgilson almost 7 years
    @EliSquared -- Yeah, that makes sense. You want to attach the Id to the return_value of your mock, not the mock itself (my bad). I've edited the code to reflect this.