Skip to content
AI & Agents
Skill

/frappe-testing-unit

Use when writing unit tests, integration tests, creating test fixtures, or running tests with bench run-tests. Prevents flaky tests from missing fixtures, incorrect test isolation, and wrong test base classes. Covers frappe.tests.utils, IntegrationTestCase, UnitTestCase, test

From plugin
frappe-claude-skill-package
17961 skills
Install
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-testing-unit --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/frappe-testing-unit

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when writing unit tests, integration tests, creating test fixtures, or running tests with bench run-tests. Prevents flaky tests from missing fixtures, incorrect test isolation, and wrong test base classes. Covers frappe.tests.utils, IntegrationTestCase, UnitTestCase, test

SKILL.md

frappe-testing-unit.SKILL.md
name: frappe-testing-unit
description: >
  Use when writing unit tests, integration tests, creating test fixtures, or running tests with bench run-tests.
  Prevents flaky tests from missing fixtures, incorrect test isolation, and wrong test base classes.
  Covers frappe.tests.utils, IntegrationTestCase, UnitTestCase, test fixtures, bench run-tests flags, test naming conventions.
  Keywords: unit test, integration test, IntegrationTestCase, fixtures, bench run-tests, frappe.tests, test_*.py, how to write test, test fixtures, run tests, test fails, bench run-tests example..
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
  author: OpenAEC-Foundation
  version: "2.0"

Unit & Integration Testing

Quick Reference

| Task | Command / Class | |------|----------------| | Run all tests | `bench --site test_site run-tests` | | Run tests for app | `bench --site test_site run-tests --app myapp` | | Run tests for doctype | `bench --site test_site run-tests --doctype "Sales Order"` | | Run single test method | `bench --site test_site run-tests --doctype "Sales Order" --test test_submit` | | Run tests for module | `bench --site test_site run-tests --module "myapp.mymodule.doctype.mydt.test_mydt"` | | Run with profiler | `bench --site test_site run-tests --doctype "Task" --profile` | | Run with failfast | `bench --site test_site run-tests --failfast` | | Generate JUnit XML | `bench --site test_site run-tests --junit-xml-output /path/report.xml` | | Skip fixture loading | `bench --site test_site run-tests --skip-test-records --skip-before-tests` | | Base class (v14) | `from frappe.tests.utils import FrappeTestCase` | | Unit test class (v15+) | `from frappe.tests.classes import UnitTestCase` | | Integration test class (v15+) | `from frappe.tests.classes import IntegrationTestCase` |

Decision Tree: Which Test Base Class?

Need to test a function or method in isolation?
├─ YES → Does it require database access?
│   ├─ NO → UnitTestCase (v15+) or FrappeTestCase (v14)
│   └─ YES → IntegrationTestCase (v15+) or FrappeTestCase (v14)
└─ NO → Need to test document lifecycle (create/submit/cancel)?
    ├─ YES → IntegrationTestCase (v15+) or FrappeTestCase (v14)
    └─ NO → Need to test permissions or user context?
        ├─ YES → IntegrationTestCase (v15+) or FrappeTestCase (v14)
        └─ NO → UnitTestCase (v15+) or FrappeTestCase (v14)

**Version note**: In v14, `FrappeTestCase` is the ONLY base class. In v15+, it still works (deprecated wrapper) but ALWAYS prefer `UnitTestCase` or `IntegrationTestCase` for new code.

Test Base Classes

FrappeTestCase (v14: still works in v15+ as compatibility wrapper)

from frappe.tests.utils import FrappeTestCase

class TestMyDoctype(FrappeTestCase):
    def test_something(self):
        doc = frappe.get_doc({"doctype": "My Doctype", "field": "value"})
        doc.insert()
        self.assertEqual(doc.field, "value")

**Behavior**: Resets `frappe.local.flags` after each test. Database transactions start before each test and rollback afterward. ALWAYS call `super().setUpClass()` if you override `setUpClass`.

UnitTestCase (v15+): No Database Access

from frappe.tests.classes import UnitTestCase

class TestMyUtils(UnitTestCase):
    def test_calculation(self):
        result = my_calculation(10, 20)
        self.assertEqual(result, 30)

    def test_html_output(self):
        html = generate_html()
        self.assertEqual(self.normalize_html(html), self.normalize_html(expected))

**Behavior**: Sets `frappe.set_user("Administrator")` in `setUpClass`. Auto-detects doctype from module path. Provides `normalize_html()`, `normalize_sql()`, `assertDocumentEqual()`, `assertQueryEqual()`, `assertSequenceSubset()`.

IntegrationTestCase (v15+): Full Database Access

from frappe.tests.classes import IntegrationTestCase

class TestSalesOrder(IntegrationTestCase):
    def test_submit_order(self):
        so = frappe.get_doc({
            "doctype": "Sales Order",
            "customer": "_Test Customer",
            "items": [{"item_code": "_Test Item", "qty": 1, "rate": 100}]
        }).insert()
        so.submit()
        self.assertEqual(so.docstatus, 1)

**Behavior**: Extends `UnitTestCase`. Calls `frappe.init()` and sets up site connection. Loads test record dependencies via `make_test_records()`. Provides `primary_connection()` and `secondary_connection()` context managers. `maxDiff = 10_000`.

Test File Structure

ALWAYS place test files in the doctype directory following this naming convention:

myapp/
└── mymodule/
    └── doctype/
        └── my_doctype/
            ├── my_doctype.py          # DocType controller
            ├── my_doctype.json        # DocType definition
            ├── test_my_doctype.py     # Test file (MUST start with test_)
            └── test_records.json      # Optional: test fixtures

**Rules**:

  • ALWAYS prefix test files with `test_` — the test runner ignores files without this prefix
  • ALWAYS use `test_{doctype_in_snake_case}.py` for doctype tests
  • NEVER place test files outside the doctype directory for doctype-specific tests
  • Non-doctype tests can live in any module, but MUST follow the `test_*.py` naming

Test Fixtures

Method 1: test_records.json (Static Fixtures)

Create a `test_records.json` file in the doctype directory:

[
    {
        "doctype": "My Doctype",
        "field1": "_Test Value 1",
        "field2": 100
    },
    {
        "doctype": "My Doctype",
        "field1": "_Test Value 2",
        "field2": 200
    }
]

**Rules**:

  • ALWAYS prefix test data values with `_Test` to distinguish from production data
  • The test runner auto-loads these before running tests for the doctype
  • Link field dependencies are resolved automatically — the runner builds records for linked DocTypes first

Method 2: _test_records List (In-Module Fixtures)

_test_records = [
Read more
Ships withfrappe-claude-skill-package

60 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations

Get the whole plugin
Stats
178
Stars
53
Forks
Maintained
Maintenance
Python
Language
2mo ago
Last commit
8mo ago
Created
14d ago
Added

Repo: Impertio-Studio/Frappe_Claude_Skill_Package