我正在尝试在不使用 FrappeTestCase 类的情况下为 Frappe Doctype 实现单元测试。我的实现在使用 bench run-tests 命令运行测试时工作正常,但在使用 pytest 时失败。这是我的 Doctype 和单元测试实现。
注释:
pytest
运行测试,而不是使用 bench
命令。bench run-tests
命令有效,但使用 pytest
无效。python -m unittest
运行测试也会给出相同的错误。文档类型实现:
class FeedbackClassification(Document):
def validate(self):
self.validate_classification_code()
def validate_classification_code(self):
if frappe.db.exists(
"Feedback Classification", {"classification_code": self.classification_code, "name": ("!=", self.name)}
):
frappe.throw(
_("Duplicate classification codes {0} already exists").format(frappe.bold(self.classification_code)),
title=_("Duplicate Classification Code"),
exc=frappe.UniqueValidationError,
)
单元测试实施:
class TestFeedbackClassification(unittest.TestCase):
def setUp(self):
with patch.object(FeedbackClassification, "__init__", lambda x: None):
self.mock_feedback_classification = FeedbackClassification()
patcher = patch(
"frappe.throw", side_effect=lambda *args, **kwargs: (_ for _ in ()).throw(UniqueValidationError(args[0]))
)
self.mock_throw = patcher.start()
self.addCleanup(patcher.stop)
self.mock_feedback_classification.classification_code = None
self.mock_feedback_classification.name = "Test Classification"
def set_classification_code(self, classification_code):
self.mock_feedback_classification.classification_code = classification_code
@patch("frappe.db.exists")
def test_validate_duplicate_classification_code(self, mock_exists):
self.set_classification_code("Classification 1")
mock_exists.return_value = True
with self.assertRaises(UniqueValidationError) as cm:
self.mock_feedback_classification.validate()
expected_message = "Duplicate classification codes <strong>Classification 1</strong> already exists"
self.assertIn(expected_message, str(cm.exception))
@patch("frappe.db.exists")
def test_validate_no_duplicate_classification_code(self, mock_exists):
self.set_classification_code("Classification")
mock_exists.return_value = False
try:
self.mock_feedback_classification.validate()
except UniqueValidationError:
self.fail("validate() raised UniqueValidationError unexpectedly!")
使用pytest运行测试时,出现以下错误:
doctype/feedback_classification/test_feedback_classification.py::TestFeedbackClassification::test_validate_duplicate_classification_code failed: /usr/local/lib/python3.11/unittest/mock.py:1372: in patched
with self.decoration_helper(patched,
/usr/local/lib/python3.11/contextlib.py:137: in __enter__
return next(self.gen)
/usr/local/lib/python3.11/unittest/mock.py:1354: in decoration_helper
arg = exit_stack.enter_context(patching)
/usr/local/lib/python3.11/contextlib.py:505: in enter_context
result = _enter(cm)
/usr/local/lib/python3.11/unittest/mock.py:1443: in __enter__
original, local = self.get_original()
/usr/local/lib/python3.11/unittest/mock.py:1406: in get_original
original = target.__dict__[name]
frappe-bench/env/lib/python3.11/site-packages/werkzeug/local.py:311: in __get__
obj = instance._get_current_object()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def _get_current_object() -> T:
try:
return get_name(local) # type: ignore[return-value]
except AttributeError:
> raise RuntimeError(unbound_message) from None
E RuntimeError: object is not bound
frappe-bench/env/lib/python3.11/site-packages/werkzeug/local.py:490: RuntimeError
我尝试过的:
预期行为:
环境:
Frappe 框架初始化应用程序上下文的方式与
bench run-tests
运行测试的方式不同。直接使用 pytest
运行测试时,可能未设置必要的 Frappe 应用程序上下文,这可能会导致错误,尤其是对于依赖此上下文的 Frappe 会话和其他本地对象。
要解决此问题,请尝试在测试设置中手动初始化 Frappe 应用程序上下文。您可以在执行测试之前使用上下文管理器建立 Frappe 环境。 这是一个可以用来解决此问题的代码模板:
进口冰沙 从冰沙进口_ 从 frappe.exceptions 导入 UniqueValidationError 导入单元测试 从unittest.mock导入补丁
类 TestFeedbackClassification(unittest.TestCase):
@classmethod
def setUpClass(cls):
# Initialize the Frappe framework
frappe.init(site='your_site.local') # Replace with your actual site.
frappe.connect()
def setUp(self):
with patch.object(FeedbackClassification, "__init__", lambda x: None):
self.mock_feedback_classification = FeedbackClassification()
patcher = patch("frappe.throw", side_effect=lambda *args, **kwargs: (_ for _ in ()).throw(UniqueValidationError(args[0])))
self.mock_throw = patcher.start()
self.addCleanup(patcher.stop)
self.mock_feedback_classification.classification_code = None
self.mock_feedback_classification.name = "Test Classification"
def tearDown(self):
pass
@classmethod
def tearDownClass(cls):
frappe.destroy()
pytest
固定装置来封装测试的设置和拆卸逻辑。