frappe-doctype-tests
Frappe DocType Tests
See frappe-doctype-schema for the .json schema.
See frappe-doctype-controller for the .py server controller.
See frappe-doctype-form-view for the .js form controller.
See frappe-doctype-list-view for the _list.js list view controller.
Full API reference, assertion helpers, context managers, and CLI flags: REFERENCE.md.
Mandatory workflow
Before writing any test, complete these steps in order:
- Read the controller (
.py) — identify hooks, validations, computed fields, whitelisted methods - Read the schema (
.json) — identify fields, child tables, link fields,is_submittable, mandatory fields - Write the test (
test_{doctype}.py) in the same doctype folder - Run with
bench run-tests --module "app_name.module.doctype.my_doctype.test_my_doctype"
File location
app_name/module/doctype/my_doctype/
├── my_doctype.json
├── my_doctype.py # controller
└── test_my_doctype.py # tests (you write this)
Test class skeleton
import frappe
from frappe.tests.utils import FrappeTestCase
class TestMyDoctype(FrappeTestCase):
def tearDown(self):
frappe.set_user("Administrator")
def test_valid_document_saves(self):
doc = make_my_doctype()
self.assertTrue(doc.name)
def test_negative_amount_rejected(self):
self.assertRaises(
frappe.ValidationError,
make_my_doctype,
amount=-1,
)
def make_my_doctype(**args):
"""Create a My Doctype with sensible defaults."""
args = frappe._dict(args)
doc = frappe.get_doc({
"doctype": "My Doctype",
"title": args.title or "Test Title",
"amount": args.amount if args.amount is not None else 100,
})
if not args.do_not_save:
doc.insert(ignore_permissions=True)
if not args.do_not_submit:
doc.submit() # only if is_submittable
return doc
make_ helper rules
Place at module level (bottom of file). Accept **args, wrap in frappe._dict(args). Provide sensible defaults for every mandatory field. Support do_not_save and do_not_submit flags. Always ignore_permissions=True. Only call submit() if is_submittable = 1.
Test naming
Name tests test_{behavior} — the class name already identifies the doctype.
6 test categories
For each controller, write tests covering applicable categories. Full examples in REFERENCE.md.
| Category | What to test | Pattern |
|---|---|---|
| Validation | Invalid input raises frappe.ValidationError |
self.assertRaises(frappe.ValidationError, make_..., bad_field=bad_value) |
| Computed fields | Derived values correct after save | make_(..., do_not_submit=True) then assertEqual |
| Lifecycle side-effects | submit/cancel/trash create/modify other docs | make_(...) then frappe.get_all(...) |
| Whitelisted methods | @frappe.whitelist() methods work correctly |
doc.method(), doc.reload(), assert |
| Link validation | Invalid links rejected | assertRaises with nonexistent link value |
| Child table logic | Validation/computation across child rows | Pass items=[...] to helper, assert totals or errors |
Submittable vs non-submittable
Check is_submittable in the doctype JSON:
- Non-submittable (
0):make_helper callsinsert()only. No submit/cancel tests. - Submittable (
1):make_helper callsinsert()thensubmit(). Test draft → submitted → cancelled flow.
Running tests
Prefer --module over --doctype — it maps directly to the test file and is unambiguous.
bench run-tests --module "app_name.module.doctype.my_doctype.test_my_doctype" # all tests
bench run-tests --module "app_name.module.doctype.my_doctype.test_my_doctype" --test test_method_name # one test
bench run-tests --module "app_name.module.doctype.my_doctype.test_my_doctype" --failfast # stop on first failure
--doctype is acceptable when you don't know the module path:
bench run-tests --doctype "My Doctype" # all tests for doctype
bench run-tests --doctype "My Doctype" --test test_method_name # one test
More from kehwar/skills
to-prd
Turn the current conversation context into a PRD and publish it to Beads. Use when user wants to create a PRD from the current context.
9setup-workflow-skills
Sets up an `## Agent skills` block in AGENTS.md/CLAUDE.md and `docs/agents/` so the engineering skills know this repo uses Beads for issue tracking and where to find domain docs. Run before first use of `to-issues`, `to-prd`, `tdd`, `improve-codebase-architecture`, or `zoom-out` — or if those skills appear to be missing context about the issue tracker or domain docs.
9tdd
Test-driven development with red-green-refactor loop. Use when user wants to build features or fix bugs using TDD, mentions "red-green-refactor", wants integration tests, or asks for test-first development. Tracks progress in Beads Issue Tracker.
8write-a-skill
Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, or build a new skill.
8grill-me
Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree. Use when user wants to stress-test a plan, get grilled on their design, or mentions "grill me".
7to-tasks
Break a plan, spec, or PRD into independently-grabbable tasks/issues on Beads Issue Tracker using tracer-bullet vertical slices. Use when user wants to convert a plan into tasks, create implementation tickets, or break down work into tasks.
7