1.简介
Frappe 提供了一些基本工具,可以快速编写自动化测试。以下是一些基本规则:
- 测试可以放在仓库中的任何位置,但必须以
test_ 开头,并且应该是一个 .py 文件。
- 测试必须在以
test_ 开头的站点上运行。这是为了防止意外丢失数据。
- 新建 DocType 时会自动生成测试桩。
- Frappe 测试运行器会自动为通过
Link 类型字段(外键)标识的依赖 DocType 构建测试记录。
- 可以使用
bench run-tests 来执行测试。
- 对于非 DocType 测试,你可以编写简单的单元测试,并在文件名前加上
test_。
为 DocType 编写测试
2.1. 编写 DocType 测试:
- 测试用例放在名为
test_[doctype].py 的文件中。
- 你必须在测试文件中创建所有依赖项。
- 创建一个 Python 模块结构来生成测试数据/依赖项。
示例(针对 test_event.py):
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
import frappe
import frappe.defaults
from frappe.tests.utils import FrappeTestCase
def create_events():
if frappe.flags.test_events_created:
return
frappe.set_user("Administrator")
doc = frappe.get_doc({
"doctype": "Event",
"subject":"_Test Event 1",
"starts_on": "2014-01-01",
"event_type": "Public"
}).insert()
doc = frappe.get_doc({
"doctype": "Event",
"subject":"_Test Event 2",
"starts_on": "2014-01-01",
"event_type": "Private"
}).insert()
doc = frappe.get_doc({
"doctype": "Event",
"subject":"_Test Event 3",
"starts_on": "2014-01-01",
"event_type": "Public"
"event_individuals": [{
"person": "[email protected]"
}]
}).insert()
frappe.flags.test_events_created = True
class TestEvent(FrappeTestCase):
def setUp(self):
create_events()
def tearDown(self):
frappe.set_user("Administrator")
def test_allowed_public(self):
frappe.set_user("[email protected]")
doc = frappe.get_doc("Event", frappe.db.get_value("Event",
{"subject":"_Test Event 1"}))
self.assertTrue(frappe.has_permission("Event", doc=doc))
def test_not_allowed_private(self):
frappe.set_user("[email protected]")
doc = frappe.get_doc("Event", frappe.db.get_value("Event",
{"subject":"_Test Event 2"}))
self.assertFalse(frappe.has_permission("Event", doc=doc))
def test_allowed_private_if_in_event_user(self):
doc = frappe.get_doc("Event", frappe.db.get_value("Event",
{"subject":"_Test Event 3"}))
frappe.set_user("[email protected]")
self.assertTrue(frappe.has_permission("Event", doc=doc))
def test_event_list(self):
frappe.set_user("[email protected]")
res = frappe.get_list("Event", filters=[["Event", "subject", "like", "_Test Event%"]], fields=["name", "subject"])
self.assertEqual(len(res), 2)
subjects = [r.subject for r in res]
self.assertTrue("_Test Event 1" in subjects)
self.assertTrue("_Test Event 3" in subjects)
self.assertFalse("_Test Event 2" in subjects)
2. 运行测试
这个函数会构建所有测试依赖项并运行你的测试。
你应该从 “frappe_bench” 文件夹运行测试。如果不加任何选项,所有测试都会被执行。
bench run-tests
如果你需要更多关于测试执行的信息,可以使用 bench 的详细日志级别。
bench –verbose run-tests
选项:
--app <appname>
--doctype
<doctype>
--test
<specifictest>
--module
<module> (Run a particular module that has tests)
--profile (Runs a Python profiler on the test)
--junit-xml-output
<pathtoxml> (The command provides test results in the standard XUnit XML format)
</pathtoxml></module></specifictest></doctype></appname>
2.1. 应用示例:
所有应用都位于文件夹:”~/frappe-bench/apps”。
我们可以为每个应用运行测试。
- frappe-bench/apps/erpnext/
- frappe-bench/apps/erpnext_demo/
- frappe-bench/apps/frappe/
bench run-tests --app erpnext
bench run-tests --app erpnext_demo
bench run-tests --app frappe
2.2. DocType 示例:
frappe@erpnext:~/frappe-bench$ bench run-tests --doctype "Activity Cost"
.
----------------------------------------------------------------------
Ran 1 test in 0.008s
OK
2.3. 测试示例:
运行 User 中的特定用例:
frappe@erpnext:~/frappe-bench$ bench run-tests --doctype User --test test_get_value
.
----------------------------------------------------------------------
Ran 1 test in 0.005s
OK
2.4. 模块示例:
如果我们想运行某个模块中的测试:
/home/frappe/frappe-bench/apps/erpnext/erpnext/support/doctype/issue/test_issue.py
我们应该这样使用模块名(相对于应用文件夹)
erpnext.support.doctype.issue.test_issue
示例:
frappe@erpnext:~/frappe-bench$ bench run-tests --module "erpnext.stock.doctype.stock_entry.test_stock_entry"
...........................
----------------------------------------------------------------------
Ran 27 tests in 30.549s
2.5. Profile 示例:
frappe@erpnext:~/frappe-bench$ bench run-tests --doctype "Activity Cost" --profile
.
----------------------------------------------------------------------
Ran 1 test in 0.010s
OK
9133 function calls (8912 primitive calls) in 0.011 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
2 0.000 0.000 0.008 0.004 /home/frappe/frappe-bench/apps/frappe/frappe/model/document.py:187(insert)
1 0.000 0.000 0.003 0.003 /home/frappe/frappe-bench/apps/frappe/frappe/model/document.py:386(_validate)
13 0.000 0.000 0.002 0.000 /home/frappe/frappe-bench/apps/frappe/frappe/database.py:77(sql)
255 0.000 0.000 0.002 0.000 /home/frappe/frappe-bench/apps/frappe/frappe/model/base_document.py:91(get)
12 0.000 0.000 0.002 0.000
2.6 不创建测试数据或 before_tests 钩子来运行测试
- 当你在开发功能时,使用
--skip-test-records 可以在不构建测试依赖项(即为关联对象构建测试数据)的情况下编写测试,这很有用。
- 你也可以使用
--skip-before-tests 跳过测试初始化脚本。
示例
bench --site school.erpnext.local run-tests --doctype "Student Group" --skip-test-records --skip-before-tests
3.0 FrappeTestCase
FrappeTestCase 是 Frappe 框架特有的 TestCase 类,继承自 unittest.TestCase。在你的测试中继承这个类可以确保:
frappe.local.flags 和其他最常用的本地代理在测试用例运行后会被重置。
- 数据库——在测试用例开始前会启动一个新的数据库事务,并在测试完成后回滚。
用法
# app/doctype/dt/test_dt.py
from frappe.tests.utils import FrappeTestCase
class TestDt(FrappeTestCase):
@classmethod
def setUpClass(cls):
super().setUpClass() # important to call super() methods when extending TestCase.
...
编写 XUnit XML 测试
如何运行:
bench run-tests --junit-xml-output=/reports/junit_test.xml
测试报告示例:
<testsuite tests="3">
<testcase classname="foo1" name="ASuccessfulTest">
<testcase classname="foo2" name="AnotherSuccessfulTest">
<testcase classname="foo3" name="AFailingTest">
<failure type="NotEnoughFoo"> details about failure </failure>
</testcase>
</testcase></testcase></testsuite>
它是为 CI Jenkins 设计的,但也适用于任何其他能理解 XUnit 格式 XML 测试结果表示的工具。
Jenkins 配置支持:
- 你应该安装 xUnit 插件——https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin
- 安装完成后,打开 Jenkins 任务配置,在 “Post-build Actions” 下点击名为 “Publish JUnit test result report” 的复选框,然后输入 XML 报告的路径:
(示例:reports/.xml*)