mcpField
T
testing

testing

github.com/lubusin测试与质量68114 次安装13 周前更新
代码仓库

关于 testing

使用 Frappe 内置测试框架编写和运行应用测试的技能,适用于 Frappe 开发者。内容涵盖 DocType、API、权限、测试数据和 Cypress UI 测试,以及测试执行与故障排查。

主要功能

  1. 01运行应用、模块和 DocType 测试
  2. 02编写 DocType 与 API 测试
  3. 03测试角色权限
  4. 04加载测试数据
  5. 05运行 Cypress UI 测试
  6. 06排查失败并隔离测试

适用场景

  1. 01验证 DocType 控制器行为
  2. 02测试工作流和 API 端点
  3. 03检查不同用户角色的访问权限
  4. 04运行 UI 测试或诊断测试套件故障

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/lubusin/frappe-skills --skill testing

安装后在 Claude Code 中输入/testing确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
ci-testing.md
cypress.md
fixtures.md
test-patterns.md
testing.md

详细介绍

Frappe 测试

使用内置测试框架为 Frappe 应用编写并运行测试。

适用场景

  • 为 DocType 控制器编写单元测试
  • 为工作流和 API 编写集成测试
  • 运行现有测试套件
  • 调试测试失败
  • 为 Frappe 应用设置 CI 流水线

所需输入

  • 要测试的应用名称
  • 执行测试所用的站点名称
  • 要测试的特定模块/DocType(可选)
  • 测试环境(开发站点、专用测试站点)

流程

0) 设置测试环境
# Install dev dependencies
bench setup requirements --dev

# Ensure site is ready
bench --site <site> migrate
1) 运行测试
# Run all tests for an app
bench --site <site> run-tests --app my_app

# Run tests for specific module
bench --site <site> run-tests --module my_app.my_module.tests

# Run tests for specific DocType
bench --site <site> run-tests --doctype "My DocType"

# Verbose output
bench --site <site> run-tests --app my_app -v

# Run single test file
bench --site <site> run-tests --module my_app.doctype.sample_doc.test_sample_doc
2) 编写 DocType 测试

在 DocType 同级目录中创建 test_<doctype_name>.py:

# my_app/doctype/sample_doc/test_sample_doc.py
import frappe
from frappe.tests.utils import FrappeTestCase

class TestSampleDoc(FrappeTestCase):
    def setUp(self):
        # Create test data
        self.doc = frappe.get_doc({
            "doctype": "Sample Doc",
            "title": "Test Document"
        }).insert()
    
    def tearDown(self):
        # Cleanup
        frappe.delete_doc("Sample Doc", self.doc.name, force=True)
    
    def test_creation(self):
        self.assertEqual(self.doc.title, "Test Document")
    
    def test_validation(self):
        doc = frappe.get_doc({
            "doctype": "Sample Doc",
            "title": ""  # Invalid - required field
        })
        self.assertRaises(frappe.ValidationError, doc.insert)
    
    def test_workflow(self):
        self.doc.status = "Approved"
        self.doc.save()
        self.assertEqual(self.doc.status, "Approved")
3) 编写 API 测试
# my_app/tests/test_api.py
import frappe
from frappe.tests.utils import FrappeTestCase

class TestAPI(FrappeTestCase):
    def test_whitelist_method(self):
        from my_app.api import process_order
        
        # Create test order
        order = frappe.get_doc({
            "doctype": "Sales Order",
            "customer": "_Test Customer"
        }).insert()
        
        # Test the API
        result = process_order(order.name, "approve")
        
        self.assertEqual(result["status"], "success")
        
        # Cleanup
        frappe.delete_doc("Sales Order", order.name, force=True)
    
    def test_permission_denied(self):
        # Test as restricted user
        frappe.set_user("[email protected]")
        
        from my_app.api import sensitive_action
        self.assertRaises(frappe.PermissionError, sensitive_action, "doc-001")
        
        # Reset user
        frappe.set_user("Administrator")
4) 测试权限
def test_role_permissions(self):
    # Create user with specific role
    user = frappe.get_doc({
        "doctype": "User",
        "email": "[email protected]",
        "roles": [{"role": "Sales User"}]
    }).insert()
    
    frappe.set_user("[email protected]")
    
    # Test permission
    self.assertTrue(frappe.has_permission("Sales Order", "read"))
    self.assertFalse(frappe.has_permission("Sales Order", "delete"))
    
    # Cleanup
    frappe.set_user("Administrator")
    frappe.delete_doc("User", user.name, force=True)
5) 使用 fixtures
# my_app/doctype/sample_doc/test_records.json
[
    {
        "doctype": "Sample Doc",
        "title": "Test Record 1"
    },
    {
        "doctype": "Sample Doc",
        "title": "Test Record 2"
    }
]

在测试中引用:

class TestSampleDoc(FrappeTestCase):
    def test_fixture_loaded(self):
        doc = frappe.get_doc("Sample Doc", "Test Record 1")
        self.assertIsNotNone(doc)
6) 运行 UI 测试(Cypress)
# Run UI tests for an app
bench --site <site> run-ui-tests my_app

# Headless mode
bench --site <site> run-ui-tests my_app --headless

验证

  • 所有测试均通过:bench --site <site> run-tests --app my_app
  • 无测试污染(测试相互隔离)
  • 测试在 < 5 分钟内运行完毕,以便快速获得反馈
  • CI 流水线在每次提交时运行测试

失败情况/调试

  • 未找到测试:确保文件名以 test_ 开头,且类名/方法名符合约定
  • 数据库错误:测试可能未隔离——检查是否缺少清理操作
  • 测试中的权限错误:在设置过程中使用 frappe.set_user("Administrator")
  • 测试运行缓慢:避免不必要的 fixtures,并模拟外部服务

升级处理

参考资料

防护措施

  • 使用测试 fixtures:通过 fixtures 加载测试数据,而不是在每个测试中手动创建
  • 清理测试数据:在 tearDown() 中删除创建的记录,或使用 frappe.db.rollback()
  • 模拟外部服务:测试中绝不调用真实 API;应模拟 HTTP 调用
  • 隔离测试:每个测试都应独立,不依赖测试执行顺序
  • 明确设置用户上下文:使用 frappe.set_user() 以特定用户身份进行测试

常见错误

错误失败原因修复方法
未使用 FrappeTestCase缺少测试设置/清理操作继承 frappe.tests.utils.FrappeTestCase
缺少数据库回滚测试污染、测试不稳定在 tearDown 中或事务中使用 frappe.db.rollback()
不稳定的异步测试间歇性失败使用 frappe.tests.utils.run_until() 或正确处理异步操作
测试实现而非行为测试脆弱测试结果,而不是内部方法调用
测试数据硬编码与现有数据冲突使用 _Test Record {uuid} 等唯一名称
跳过权限测试存在安全漏洞使用不同用户角色进行测试,而不只是使用 Administrator

评论

支持 Markdown