smithery.ai

frappe-integration-test-generator

Generate integration tests for multi-DocType workflows in Frappe. Use when testing end-to-end workflows, state transitions, or complex business processes.

First seen Apr 13, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 7,802 B
  • docs SUMMARY.md 195 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Frappe Integration Test Generator

Generate comprehensive integration tests for multi-DocType workflows and end-to-end business processes in Frappe.

Global Rules

These Frappe conventions apply to everything this skill generates, and override any conflicting example below.

  • Bench commands: use bare bench (never ./env/bin/bench or a full path). Always pass --site <site> explicitly — never run a bare bench migrate / bench run-tests. Run bench start in the background and only if it isn't already running. Don't run discovery commands (which bench, bench --version).
  • DocType files live at apps/<app>/<app>/<module>/doctype/<name>/<name>.json — the app name appears twice (directory + Python package) — with an empty init.py alongside. Never mkdir the folder; write the JSON and run bench --site <site> migrate to create the structure. Don't add creation, modified, owner, modified_by, or docstatus as fields — Frappe manages them.
  • Database & ORM: prefer frappe.qb.getquery() over raw frappe.db.sql(). Use frappe.db.getall() for server logic (ignores permissions) and frappe.db.getlist() for user-facing APIs (enforces them). Never use frappe.db.setvalue() on a field with validation or lifecycle logic — load the doc and doc.save() so controller hooks run. Batch-fetch related records; never query inside a loop (N+1).
  • Never call frappe.db.commit() in controllers, request handlers, background jobs, or patches — Frappe auto-commits on success and rolls back on uncaught errors. Flush manually only to make a write visible to a subsequent frappe.enqueue() (or pass enqueueaftercommit=True).
  • Permissions & APIs: put permission checks inside controller methods (enforced on every call path), not in API wrappers. Type-hint every @frappe.whitelist() parameter so Frappe validates and casts it, and pass methods=[...] to pin the HTTP verb.

When to Use This Skill

Claude should invoke this skill when:

  • User wants to test complete workflows
  • User needs end-to-end scenario testing
  • User mentions integration tests or workflow testing
  • User wants to test multi-DocType interactions
  • User needs to verify business process integrity

Test Site

Run tests on a separate site from the dev site — integration tests create, modify, and delete data across many DocTypes. If the dev site is app.localhost, create app-test.localhost, install the app there, and run tests against it:

bench new-site app-test.localhost --admin-password admin
bench --site app-test.localhost install-app <app>
bench --site app-test.localhost run-tests --app <app>

Always pass --site — never run a bare bench run-tests. If a test fails with "DocType not found", run bench --site app-test.localhost migrate first.

Conventions

  • Inherit from frappe.tests.IntegrationTestCase (multi-DocType workflows always touch the DB).
  • Each test runs inside a transaction that auto-rolls-back — do not write manual teardown/cleanup or call frappe.db.rollback().
  • For permission-context tests, switch the active user with frappe.setuser("[email protected]") and restore it (e.g. frappe.setuser("Administrator")) at the end of the test.
  • Mock or avoid real external API calls in tests — use unittest.mock.patch so tests stay deterministic and offline.
  • Feature-wise tests spanning multiple DocTypes live at apps/<app>/<app>/tests/test<feature>.py; a workflow test centred on one DocType can live alongside it at apps/<app>/<app>/<module>/doctype/<doctype>/test<doctype>.py.

Capabilities

1. Workflow Integration Test

Complete Sales Workflow Test:

import frappe
from frappe.tests import IntegrationTestCase

class TestSalesWorkflow(IntegrationTestCase):
    def test_complete_sales_cycle(self):
        """Test end-to-end sales process"""
        # 1. Create Customer
        customer = self._create_test_customer()

        # 2. Create Sales Order
        so = self._create_sales_order(customer.name)
        so.submit()

        # 3. Create Sales Invoice from SO
        si = self._make_sales_invoice_from_order(so.name)
        si.insert()
        si.submit()

        # 4. Create Payment Entry
        pe = self._create_payment_entry(si)
        pe.insert()
        pe.submit()

        # Verify workflow completed
        si.reload()
        self.assertEqual(si.status, 'Paid')
        self.assertEqual(si.outstanding_amount, 0)

    def _create_test_customer(self):
        return frappe.get_doc({
            'doctype': 'Customer',
            'customer_name': '_Test Customer',
            'customer_group': 'Commercial'
        }).insert()

    def _create_sales_order(self, customer):
        return frappe.get_doc({
            'doctype': 'Sales Order',
            'customer': customer,
            'delivery_date': frappe.utils.add_days(frappe.utils.today(), 7),
            'items': [{
                'item_code': '_Test Item',
                'qty': 10,
                'rate': 100
            }]
        })

2. State Transition Test

Test Document States:

class TestInvoiceStates(IntegrationTestCase):
    def test_invoice_state_transitions(self):
        """Test all possible state transitions"""
        si = self._get_test_invoice()

        # Draft state
        si.insert()
        self.assertEqual(si.docstatus, 0)
        self.assertEqual(si.status, 'Draft')

        # Submit transition
        si.submit()
        self.assertEqual(si.docstatus, 1)
        self.assertEqual(si.status, 'Submitted')

        # Cannot edit submitted
        si.customer = 'Different Customer'
        with self.assertRaises(frappe.ValidationError):
            si.save()

        # Cancel transition
        si.cancel()
        self.assertEqual(si.docstatus, 2)
        self.assertEqual(si.status, 'Cancelled')

3. Permission-Context Test

Switch the active user to assert a workflow behaves correctly per role, then restore the user. The auto-rollback transaction cleans up any documents created.

class TestApprovalPermissions(IntegrationTestCase):
    def test_only_manager_can_approve(self):
        """A regular user cannot approve; a manager can"""
        expense = self._create_test_expense()

        # Regular user is denied
        frappe.set_user("[email protected]")
        with self.assertRaises(frappe.PermissionError):
            expense.approve()

        # Manager succeeds
        frappe.set_user("[email protected]")
        expense.reload()
        expense.approve()
        self.assertEqual(expense.status, "Approved")

        # Restore the default test user
        frappe.set_user("Administrator")

4. Mocking External Calls

Never hit a real external service from a test. Patch the call so the test stays deterministic and offline.

from unittest.mock import patch

class TestPaymentSync(IntegrationTestCase):
    @patch("my_app.gateway.charge_card")
    def test_payment_marks_invoice_paid(self, mock_charge):
        mock_charge.return_value = {"status": "succeeded"}
        si = self._create_test_invoice()
        si.collect_payment()
        self.assertEqual(si.status, "Paid")
        mock_charge.assert_called_once()

References

Integration Test Examples: