测试时注入 Python 类,而不修改类

56 阅读2分钟

在不修改生产流水线的情况下,我们需要为生产流水线创建单元测试代码。流水线有用于读写队列的方法。我们正在使用 mock 来覆盖这些调用以创建单独的“单元”测试。但是,我们遇到了最后一个问题。

我们需要访问流水线中创建的对象,但创建该对象的方法不返回该对象。将对象设置为 self 以便在方法返回后仍保留在内存中也是不可接受的。

我们想知道是否有办法在类方法运行时将其注入,以便我们可以在方法返回之前检索生产对象。

class production(object):
    def __init__(self):
        self.object_b = 'Test Object'

    def run(self):
        "Other lines of code"
        object_a = self.create_production_object()
        "Other lines of code"
        "Test object here"
        return 0

    def create_production_object(self):
        return 'Production Object'

test_prod_class = production()
test_prod_class.run()
assert(test_prod_class.object_a, 'Production Object')

2、解决方案:

方法一:重写创建对象的函数 我们可以通过重写创建对象的函数,使其在创建对象的同时也将对象存储在 self 中,从而解决问题。

class MockProduction(production):

    def create_production_object(self):
        self.object_a = super(MockProduction, self).create_production_object()
        return self.object_a

方法二:使用 inspect 模块 另一个解决方案是使用 inspect 模块来检索和修改类的方法。可以通过以下步骤实现:

  1. 使用 inspect.getsourcefile() 获取类文件的路径。
  2. 将类文件的路径加载到内存中。
  3. 使用 inspect.getsource() 获取类的方法源代码。
  4. 将方法源代码修改为所需的代码。
  5. 将修改后的方法源代码写回类文件中。

该方法的代码示例如下:

import ast
import inspect

class production(object):
    def __init__(self):
        self.object_b = 'Test Object'

    def run(self):
        "Other lines of code"
        object_a = self.create_production_object()
        "Other lines of code"
        "Test object here"
        return 0

    def create_production_object(self):
        return 'Production Object'

# 获取类文件的路径
filepath = inspect.getsourcefile(production)

# 将类文件的路径加载到内存中
with open(filepath, 'r') as f:
    source = f.read()

# 获取类的方法源代码
tree = ast.parse(source)

# 找到需要修改的方法
method_to_modify = tree.body[1]

# 将方法源代码修改为所需的代码
method_to_modify.body.insert(1, ast.Assign(targets=[ast.Name(id='self', ctx=ast.Store())], value=ast.Call(func=ast.Attribute(value=ast.Name(id='super', ctx=ast.Load()), attr='create_production_object', ctx=ast.Load()), args=[], keywords=[])))

# 将修改后的方法源代码写回类文件中
with open(filepath, 'w') as f:
    f.write(ast.unparse(tree))

# 重新加载类
reload(production)

# 创建新的类实例
test_prod_class = production()

# 运行类方法
test_prod_class.run()

# 断言对象是否创建成功
assert(test_prod_class.object_a, 'Production Object')