Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking a property call returning MagicMock, not value

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
like image 589
EliSquared Avatar asked Jun 16 '17 15:06

EliSquared


1 Answers

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.

like image 61
mgilson Avatar answered Nov 01 '22 18:11

mgilson