我写了一个cronjob,它遍历一个帐户列表并为它们执行一些Web调用(如下所示):
for account in self.ActiveAccountFactory():
try:
self.logger.debug('Updating %s', account.login)
self.update_account_from_fb(account)
self.check_balances()
self.check_rois()
except Exception,e:
self.logger.exception(traceback.format_exc())
因为这个作业每10分钟由heroku运行一次,所以我不希望整个作业失败只是因为一个帐户遇到了问题(它发生了)。我在这里放置了一个try catch子句,以便这个任务是“容错的”。
然而,我注意到,当我测试时,这个try / catch块给了我神秘的问题,因为即使存在一些严重的错误,也允许任务继续执行。
在测试期间禁用try / except块的最佳方法是什么?
我想直接实现这样的代码:
for account in self.ActiveAccountFactory():
self.logger.debug('Updating %s', account.login)
self.update_account_from_fb(account)
self.check_balances()
self.check_rois()
self.logger.exception(traceback.format_exc())
在我的测试用例中但是这使我的测试非常笨拙,因为我正在复制大量的代码。
我该怎么办?
答案 0 :(得分:1)
首先:不要使用except Exception
吞下所有例外情况。这是一个糟糕的设计。所以切掉它。
解决这个问题:
您可以做的一件事是为logger.exception
方法设置monkeypatch。然后,您可以根据它是否被调用,是否创建模拟记录器或单独的测试记录器,或者在发生某些异常时停止测试的自定义测试记录器类来处理您认为合适的测试。您甚至可以选择通过引发错误立即结束测试。
以下是使用pytest.monkeypatch
的示例。我喜欢pytest
这样做的方法,因为他们已经为它预先设置了夹具设置,并且不需要样板代码。但是,还有其他方法可以执行此操作(例如使用unittest.mock.patch
作为unitest
module的一部分)。
我会打电话给你的班级SomeClass
。我们要做的是创建一个SomeClass
对象的修补版本作为夹具。修补后的版本不会记录到记录器;相反,它将有一个模拟记录器。记录器发生的任何事情都将记录在模拟记录器中以供日后检查。
import pytest
import unittest.mock as mock # import mock for Python 2
@pytest.fixture
def SomeClassObj_with_patched_logger(monkeypatch):
##### SETUP PHASE ####
# create a basic mock logger:
mock_logger = mock.Mock(spec=LoggerClass)
# patch the 'logger' attribute so that when it is called on
# 'some_class_instance' (which is bound to 'self' in the method)
# things are re-routed to mock_logger
monkeypatch.setattr('some_class_instance.logger', mock_logger)
# now create class instance you will test with the same name
# as the patched object
some_class_instance = SomeClass()
# the class object you created will now be patched
# we can now send that patched object to any test we want
# using the standard pytest fixture way of doing things
yield some_class_instance
###### TEARDOWN PHASE #######
# after all tests have been run, we can inspect what happened to
# the mock logger like so:
print('\n#### ', mock_logger.method_calls)
如果模拟记录器的方法调用中出现call.exception
,则表示已调用该方法。还有很多其他方法可以解决这个问题,这只是一个。
如果您正在使用logging
模块,则LoggerClass
应为logging.Logger
。或者,您可以mock_logger = mock.Mock()
。或者,您可以创建自己的自定义测试记录器类,在调用其exception
方法时引发异常。天空才是极限!
在任何测试中使用修补对象:
def test_something(SomeClassObj_with_patched_logger):
# no need to do the line below really, just getting
# a shorter variable name
my_obj = SomeClassObj_with_patched_logger
#### DO STUFF WITH my_obj #####
如果您不熟悉pytest
,请参阅this training video了解更多深度信息。
答案 1 :(得分:0)
try...except
块是很困难的,因为它们会捕获并试图处理你真正宁愿看到的错误。你已经发现了。在测试时,
except Exception as e:
(不要使用Exception,e
,它不能向前兼容)替换在您的情况下不太可能发生的异常类型,例如
except AssertionError as e:
文本编辑器会为您执行此操作(之后将其反转),只需点击几下鼠标即可。
答案 2 :(得分:0)
您可以通过添加_testing=False
参数来使callables测试感知。用于在测试时对可调用的备用路径进行编码。然后在从测试文件调用时传递_testing = True。
对于此问题中提供的情况,将if _testing: raise
置于异常正文中将“解除”异常。
调节模块级别代码是tricker。为了在测试包mod
中的模块pack
时获得特殊行为,我将
_testing = False # in `pack.__init__`
from pack import _testing # in pack.mod
然后test_mod
我提出了类似的内容:
import pack
pack._testing = True
from pack import mod