单元测试

1.简介

Frappe 提供了一些基本工具,可以快速编写自动化测试。以下是一些基本规则:

  1. 测试可以放在仓库中的任何位置,但必须以 test_ 开头,并且应该是一个 .py 文件。
  2. 测试必须在以 test_ 开头的站点上运行。这是为了防止意外丢失数据。
  3. 新建 DocType 时会自动生成测试桩。
  4. Frappe 测试运行器会自动为通过 Link 类型字段(外键)标识的依赖 DocType 构建测试记录。
  5. 可以使用 bench run-tests 来执行测试。
  6. 对于非 DocType 测试,你可以编写简单的单元测试,并在文件名前加上 test_。

为 DocType 编写测试

2.1. 编写 DocType 测试:

  1. 测试用例放在名为 test_[doctype].py 的文件中。
  2. 你必须在测试文件中创建所有依赖项。
  3. 创建一个 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。在你的测试中继承这个类可以确保:

  1. frappe.local.flags 和其他最常用的本地代理在测试用例运行后会被重置。
  2. 数据库——在测试用例开始前会启动一个新的数据库事务,并在测试完成后回滚。

用法

# 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 配置支持:

  1. 你应该安装 xUnit 插件——https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin
  2. 安装完成后,打开 Jenkins 任务配置,在 “Post-build Actions” 下点击名为 “Publish JUnit test result report” 的复选框,然后输入 XML 报告的路径:

(示例:reports/.xml*)