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>
我对应该在哪里嘲笑此功能一无所知?
答案 0 :(得分:2)
在mock
修补year_in_range
时,为时已晚。 mock.patch
导入由您提供的字符串指定的模块,并修补模块中指示的名称,以便它引用模拟对象-它从根本上不会改变功能对象本身。导入scuevals_api.resources.students
时,将执行StudentsResource
类的主体,并在year_in_range
对象中保存对原始StudentResource.args['graduation_year']
的引用,因此将其命名为{{ 1}}引用模拟对象没有影响。
在这种情况下,您有几种选择:
year_in_range
,则可以使用测试条件的数据为数据库(?)播种year_in_range
将调用的datetime.now
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函数运行之前(在测试期间),该引用将无法访问)。