模拟整个 python class

Mock entire python class

我想在 python 中做一个简单的测试,但我无法弄清楚如何完成模拟过程。

这是 class 和 def 代码:

class FileRemoveOp(...)
    @apply_defaults
    def __init__(
            self,
            source_conn_keys,
            source_conn_id='conn_default',
            *args, **kwargs):
        super(v4FileRemoveOperator, self).__init__(*args, **kwargs)
        self.source_conn_keys = source_conn_keys
        self.source_conn_id = source_conn_id


    def execute (self, context)
          source_conn = Connection(conn_id)
          try:
              for source_conn_key in self.source_keys:
                  if not source_conn.check_for_key(source_conn_key):    
                      logging.info("The source key does not exist")  
                  source_conn.remove_file(source_conn_key,'')
          finally:
              logging.info("Remove operation successful.")

这是我对执行函数的测试:

@mock.patch('main.Connection')
def test_remove_execute(self,MockConn):
    mock_coon = MockConn.return_value
    mock_coon.value = #I'm not sure what to put here#
    remove_operator = FileRemoveOp(...)
    remove_operator.execute(self)

由于 execute 方法尝试建立连接,我需要模拟它,我不想建立真正的连接,只是 return 一些模拟.我该怎么做?我习惯于在 Java 中进行测试,但我从未在 python 中进行过测试。

首先,了解您始终需要在 unittest.mock 文档中所述使用您尝试模拟的东西的地方进行模拟是非常重要的。

The basic principle is that you patch where an object is looked up, which is not necessarily the same place as where it is defined.

接下来您需要做的是 return 一个 MagicMock 实例作为修补对象的 return_value。因此,为了总结这一点,您需要使用以下序列。

  • 补丁对象
  • 准备MagicMock以供使用
  • return 我们刚刚创建的 MagicMock return_value

这里是一个项目的简单示例。

connection.py(Class我们想Mock)

class Connection(object):                                                        
    def execute(self):                                                           
        return "Connection to server made"

file.py(使用Class的地方)

from project.connection import Connection                                        


class FileRemoveOp(object):                                                      
    def __init__(self, foo):                                                     
        self.foo = foo                                                           

    def execute(self):                                                           
        conn = Connection()                                                      
        result = conn.execute()                                                  
        return result    

tests/test_file.py

import unittest                                                                  
from unittest.mock import patch, MagicMock                                       
from project.file import FileRemoveOp                                            

class TestFileRemoveOp(unittest.TestCase):                                       
    def setUp(self):                                                             
        self.fileremoveop = FileRemoveOp('foobar')                               

    @patch('project.file.Connection')                                            
    def test_execute(self, connection_mock):
        # Create a new MagickMock instance which will be the
        # `return_value` of our patched object                                     
        connection_instance = MagicMock()                                        
        connection_instance.execute.return_value = "testing"

        # Return the above created `connection_instance`                     
        connection_mock.return_value = connection_instance                       

        result = self.fileremoveop.execute()                                     
        expected = "testing"                                                     
        self.assertEqual(result, expected)                                       

    def test_not_mocked(self):
        # No mocking involved will execute the `Connection.execute` method                                                   
        result = self.fileremoveop.execute()                                     
        expected = "Connection to server made"                                   
        self.assertEqual(result, expected) 

我发现这个简单的解决方案在 python3 中有效:您可以在第一次导入之前替换整个 class。假设我必须从 real.manager

中模拟 class 'Manager'
class MockManager:
    ...

import real.manager
real.manager.Manager = MockManager

如果没有更好的地方,可以在init.py 中进行替换。 它也可能在 python2 中工作,但我没有检查。