模拟在类变量初始化期间作为参数传入的函数

时间:2019-01-21 02:35:32

标签: python unit-testing mocking python-unittest

here

def year_in_range(year):
    return datetime.now().year <= year <= datetime.now().year + 10


class StudentsResource(Resource):
    args = {
        'graduation_year': fields.Int(required=True, validate=year_in_range),
    }

    ...

我正在尝试模拟year_in_range(以始终返回True),但是到目前为止,我的所有尝试都失败了。

我正在将装饰器方法与mock.patch一起使用,并尝试了很多不同的目标,但是我认为应该是正确的目标是: @mock.patch('scuevals_api.resources.students.year_in_range', return_value=True)

模拟函数永远不会被调用,因为它不能正确模拟。我也没有任何错误。

我唯一剩下的怀疑是,该函数与参数作为参数传递给fields.Int(因此出现问题标题)有关,但是在我看来,它不应该影响任何事情。 / p>

我对应该在哪里嘲笑此功能一无所知?

2 个答案:

答案 0 :(得分:2)

mock修补year_in_range时,为时已晚。 mock.patch导入由您提供的字符串指定的模块,并修补模块中指示的名称,以便它引用模拟对象-它从根本上不会改变功能对象本身。导入scuevals_api.resources.students时,将执行StudentsResource类的主体,并在year_in_range对象中保存对原始StudentResource.args['graduation_year']的引用,因此将其命名为{{ 1}}引用模拟对象没有影响。

在这种情况下,您有几种选择:

  1. 假设您要测试一些功能,而不是尝试模拟year_in_range,则可以使用测试条件的数据为数据库(?)播种
  2. 您可以修补year_in_range将调用的datetime.now
  3. 您可以修补year_in_range成员,该成员已保存传递给StudentResource.args['graduation_year']的函数。

答案 1 :(得分:1)

由于克里斯·亨特(Chris Hunt)的解释,我想出了一个替代解决方案。它确实修改了应用程序代码而不是测试代码,但是如果可以接受的话(在当今时代应该是这样,因为拥有可测试的代码是高优先级),这是一个非常简单的解决方案:

无法模拟year_in_range,因为在完成模拟之前会保存对原始函数的引用。因此,将要模拟的函数与另一个函数“包装”在一起,然后传递包装器。可以使用lambda函数以一种简洁的方式进行包装:

def year_in_range(year):
    return datetime.now().year <= year <= datetime.now().year + 10


class StudentsResource(Resource):
    args = {
        'graduation_year': fields.Int(required=True, validate=lambda y: year_in_range(y)),
    }

    ...

现在,当我按照问题所述嘲笑year_in_range时,它将起作用。原因是因为现在引用保存到了lambda函数,而不是原始的year_in_range(在lambda函数运行之前(在测试期间),该引用将无法访问)。