如何将方法修补到 Mock() 中?

How to patch a method into a Mock()?

我有一个功能要测试。 datahandler 参数是我在模块其他地方定义的 class Datahandler 的一个实例。

def create_row(row_content, datahandler):
  row = {}
  # Here comes some code that fills the "row" dictionary based on what was passed in row_content
  row = datahandler.process_row(row)
  # some more handling happens to the processed row 
  return row

我尝试编写单元测试,但我遇到了困难,因为我似乎无法找到一种方法来替换模拟数据处理程序的 process_row 方法:

class TestCreateRow(unittest.TestCase):

  def fake_process_row(row): 
    return row

  def test_create_row(self):
    # create a suitable row_content here
    # row_content = ...
    expected = {"first column":"first value", "second column":"second value"}
    datahandler = Mock()
    # !!! This doesn't work - what is the correct solution?
    datahandler.process_row = self.fake_process_row
    result = create_row(row_content, datahandler)
    self.assertDictEqual(expected, result)

我尝试使用 return_valueside_effectsfake_process_row 函数分配给模拟数据处理程序,甚至将 fake_process_row 修补为 Mock 但是对于我尝试过的任何合成正确的版本,调用 datahandler.process_row(row) 继续 return a Mock(),当我需要它实际调用我的 fake_process_row 函数时,它可以 return a实排.

我见过一些解决方案,在测试中,创建了一个真实的 datahandler 对象,并将 fake_process_row 修补到其中。我不想这样做,因为真正的 class 实例化并不是完全微不足道的。只使用一种“工作”方法进行模拟会容易得多,就像我可以使用 属性.

的“工作”return 值进行模拟一样

所以我找到了解决方案,那就是创建两个单独的模拟。

  def test_create_row(self):
    # create a suitable row_content here
    # row_content = ...
    expected = {"first column":"first value", "second column":"second value"}
    datahandler = Mock()
    # The magic happens here 
    method_mock = Mock(side_effect = self.fake_process_row)
    datahandler.process_row = method_mock
    result = create_row(row_content, datahandler)
    self.assertDictEqual(expected, result)

使用 side_effect 有点违反直觉,但一旦访问 Mock() 就会发生这种情况。所以当我们将datahandler.process_row设置为具有这种副作用的Mock时,它会在被测代码调用process_row方法时触发,它也足够聪明,可以正确传递参数。