Frappe provides some basic tooling to write automated tests. There are some basic rules:

  1. Test can be anywhere in your repository but must begin with test_ and should be a .py file.
  2. The test runner will automatically build test records for dependent DocTypes identified by the Link type field (Foreign Key).
  3. For non-DocType tests, you can write simple unit tests and prefix your file names with test_.

Writing Tests

When you create a new DocType (in developer mode), the boilerplate files also contain the test_{doctype}.py file. The test file should handle creating dependencies and cleaning them up.

Here is a sample test file referred from test_event.py.

import frappe
import unittest

def create_events():
    if frappe.flags.test_events_created:

    doc = frappe.get_doc({
        "doctype": "Event",
        "subject":"_Test Event 1",
        "starts_on": "2014-01-01",
        "event_type": "Public"

    doc = frappe.get_doc({
        "doctype": "Event",
        "subject":"_Test Event 3",
        "starts_on": "2014-01-01",
        "event_type": "Public"
        "event_individuals": [{
            "person": "test1@example.com"

    frappe.flags.test_events_created = True

class TestEvent(unittest.TestCase):
    def setUp(self):

    def tearDown(self):

    def test_allowed_public(self):
        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):
        doc = frappe.get_doc("Event", frappe.db.get_value("Event",
            {"subject":"_Test Event 2"}))
        self.assertFalse(frappe.has_permission("Event", doc=doc))

Writing Tests for Commands

To write tests for your Bench commands, you can group your tests under a Class that extends BaseTestCommands from frappe.tests.test_commands and unittest.TestCase so that it runs during the bench run-tests command.

For reference, here is are some tests written for the bench execute command.

class TestCommands(BaseTestCommands, unittest.TestCase):
    def test_execute(self):
        # test 1: execute a command expecting a numeric output
        self.execute("bench --site {site} execute frappe.db.get_database_size")
        self.assertEquals(self.returncode, 0)
        self.assertIsInstance(float(self.stdout), float)

        # test 2: execute a command expecting an errored output as local won't exist
        self.execute("bench --site {site} execute frappe.local.site")
        self.assertEquals(self.returncode, 1)

        # test 3: execute a command with kwargs
        # Note:
        # terminal command has been escaped to avoid .format string replacement
        # The returned value has quotes which have been trimmed for the test
        self.execute("""bench --site {site} execute frappe.bold --kwargs '{{"text": "DocType"}}'""")
        self.assertEquals(self.returncode, 0)
        self.assertEquals(self.stdout[1:-1], frappe.bold(text='DocType'))

Running Tests

Run the following command to run all your tests. It will build all the test dependencies once and run your tests. You should run tests from frappe_bench folder.

# run all tests
bench --site [sitename] run-tests

# run tests for only frappe app
bench --site [sitename] run-tests --app frappe

# run tests for the Task doctype
bench --site [sitename] run-tests --doctype "Task"

# run a test using module path
bench --site [sitename] run-tests --module frappe.tests.test_api

# run a specific test from a test file
bench --site [sitename] run-tests --module frappe.tests.test_api --test test_insert_many

# run tests without creating test records
bench --site [sitename] run-tests --skip-test-records --doctype "Task"

# profile tests and show a report after tests execute
bench --site [sitename] run-tests --profile --doctype "Task"
Ran 1 test in 0.010s


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

# verbose log level for tests
bench --site [sitename] --verbose run-tests