refactor: clarify test record dep management in test modules (#28060)
This commit is contained in:
parent
bd06784d1b
commit
83bc1f09e9
20 changed files with 153 additions and 65 deletions
|
|
@ -5,7 +5,7 @@ from frappe.contacts.doctype.contact.contact import get_full_name
|
|||
from frappe.email import get_contact_list
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
test_dependencies = ["Contact", "Salutation"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Contact", "Salutation"]
|
||||
|
||||
|
||||
class UnitTestContact(UnitTestCase):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import frappe.share
|
|||
from frappe.automation.doctype.auto_repeat.test_auto_repeat import create_submittable_doctype
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
test_dependencies = ["User"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User"]
|
||||
|
||||
|
||||
class UnitTestDocshare(UnitTestCase):
|
||||
|
|
|
|||
|
|
@ -5,6 +5,13 @@
|
|||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record depdendencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
|
||||
|
||||
class Test{classname}(UnitTestCase):
|
||||
"""
|
||||
Unit tests for {classname}.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from frappe.desk.reportview import save_report as _save_report
|
|||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
test_records = frappe.get_test_records("Report")
|
||||
test_dependencies = ["User"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User"]
|
||||
|
||||
|
||||
class UnitTestReport(UnitTestCase):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
test_dependencies = ["Role"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Role"]
|
||||
|
||||
|
||||
class UnitTestRoleProfile(UnitTestCase):
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from frappe.core.doctype.doctype.test_doctype import new_doctype
|
|||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
from frappe.tests.utils import make_test_records_for_doctype
|
||||
|
||||
test_dependencies = ["Custom Field", "Property Setter"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Custom Field", "Property Setter"]
|
||||
|
||||
|
||||
class UnitTestCustomizeForm(UnitTestCase):
|
||||
|
|
|
|||
|
|
@ -290,15 +290,6 @@ def validate_roles(self):
|
|||
self.populate_role_profile_roles()
|
||||
|
||||
|
||||
@deprecated(
|
||||
"frappe.tests_runner.get_dependencies", "2024-20-08", "v17", "use frappe.tests.utils.get_dependencies"
|
||||
)
|
||||
def test_runner_get_dependencies(doctype):
|
||||
from frappe.tests.utils import get_dependencies
|
||||
|
||||
return get_dependencies(doctype)
|
||||
|
||||
|
||||
@deprecated("frappe.tests_runner.get_modules", "2024-20-08", "v17", "use frappe.tests.utils.get_modules")
|
||||
def test_runner_get_modules(doctype):
|
||||
from frappe.tests.utils import get_modules
|
||||
|
|
@ -530,3 +521,48 @@ def model_trace_traced_field_context(*args, **kwargs):
|
|||
from frappe.tests.classes.context_managers import trace_fields
|
||||
|
||||
return trace_fields(*args, **kwargs)
|
||||
|
||||
|
||||
@deprecated(
|
||||
"frappe.tests.utils.get_dependencies",
|
||||
"2024-20-09",
|
||||
"v17",
|
||||
"refactor to use frappe.tests.utils.get_missing_records_doctypes",
|
||||
)
|
||||
def tests_utils_get_dependencies(doctype):
|
||||
"""Get the dependencies for the specified doctype"""
|
||||
import frappe
|
||||
from frappe.tests.utils.generators import get_modules
|
||||
|
||||
module, test_module = get_modules(doctype)
|
||||
meta = frappe.get_meta(doctype)
|
||||
link_fields = meta.get_link_fields()
|
||||
|
||||
for df in meta.get_table_fields():
|
||||
link_fields.extend(frappe.get_meta(df.options).get_link_fields())
|
||||
|
||||
options_list = [df.options for df in link_fields]
|
||||
|
||||
if hasattr(test_module, "test_dependencies"):
|
||||
options_list += test_module.test_dependencies
|
||||
|
||||
options_list = list(set(options_list))
|
||||
|
||||
if hasattr(test_module, "test_ignore"):
|
||||
for doctype_name in test_module.test_ignore:
|
||||
if doctype_name in options_list:
|
||||
options_list.remove(doctype_name)
|
||||
|
||||
options_list.sort()
|
||||
|
||||
return options_list
|
||||
|
||||
|
||||
@deprecated(
|
||||
"frappe.tests_runner.get_dependencies",
|
||||
"2024-20-08",
|
||||
"v17",
|
||||
"refactor to use frappe.tests.utils.get_missing_record_doctypes",
|
||||
)
|
||||
def test_runner_get_dependencies(doctype):
|
||||
return tests_utils_get_dependencies(doctype)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from frappe.model.db_query import DatabaseQuery
|
|||
from frappe.permissions import add_permission, reset_perms
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
test_dependencies = ["User"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User"]
|
||||
|
||||
|
||||
class UnitTestTodo(UnitTestCase):
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from frappe.tests import IntegrationTestCase, UnitTestCase
|
|||
|
||||
from .notification import trigger_notifications
|
||||
|
||||
test_dependencies = ["User", "Notification"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User", "Notification"]
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
test_dependencies = ["User", "Connected App", "Token Cache"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User", "Connected App", "Token Cache"]
|
||||
|
||||
|
||||
class UnitTestTokenCache(UnitTestCase):
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from contextlib import AbstractContextManager, contextmanager
|
|||
import frappe
|
||||
from frappe.utils import cint
|
||||
|
||||
from ..utils.generators import make_test_records
|
||||
from ..utils.generators import get_missing_records_module_overrides, make_test_records
|
||||
from .unit_test_case import UnitTestCase
|
||||
|
||||
logger = logging.Logger(__file__)
|
||||
|
|
@ -47,10 +47,11 @@ class IntegrationTestCase(UnitTestCase):
|
|||
cls._newly_created_test_records = []
|
||||
if cls.doctype and cls.doctype not in frappe.local.test_objects:
|
||||
cls._newly_created_test_records += make_test_records(cls.doctype)
|
||||
for doctype in getattr(cls.module, "test_dependencies", []):
|
||||
if doctype not in frappe.local.test_objects:
|
||||
elif not cls.doctype:
|
||||
to_add, to_remove = get_missing_records_module_overrides(cls.module)
|
||||
to_add.difference_update(to_remove)
|
||||
for doctype in to_add:
|
||||
cls._newly_created_test_records += make_test_records(doctype)
|
||||
|
||||
# flush changes done so far to avoid flake
|
||||
frappe.db.commit()
|
||||
if cls.SHOW_TRANSACTION_COMMIT_WARNINGS:
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ from frappe.tests import IntegrationTestCase
|
|||
from frappe.tests.test_query_builder import db_type_is, run_only_if
|
||||
from frappe.utils.testutils import add_custom_field, clear_custom_fields
|
||||
|
||||
test_dependencies = ["User", "Blog Post", "Blog Category", "Blogger"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["User", "Blog Post", "Blog Category", "Blogger"]
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from frappe.query_builder.utils import db_type_is
|
|||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.tests.test_query_builder import run_only_if
|
||||
|
||||
test_dependencies = ["Email Account"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Email Account"]
|
||||
|
||||
|
||||
class TestEmail(IntegrationTestCase):
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from frappe.desk.form.load import get_docinfo, getdoc, getdoctype
|
|||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.utils.file_manager import save_file
|
||||
|
||||
test_dependencies = ["Blog Category", "Blogger"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Blog Category", "Blogger"]
|
||||
|
||||
|
||||
class TestFormLoad(IntegrationTestCase):
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ from frappe.tests import IntegrationTestCase
|
|||
from frappe.tests.utils import make_test_records_for_doctype
|
||||
from frappe.utils.data import now_datetime
|
||||
|
||||
test_dependencies = ["Blogger", "Blog Post", "User", "Contact", "Salutation"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Blogger", "Blog Post", "User", "Contact", "Salutation"]
|
||||
|
||||
|
||||
class TestPermissions(IntegrationTestCase):
|
||||
|
|
|
|||
|
|
@ -47,3 +47,6 @@ from frappe.deprecation_dumpster import (
|
|||
from frappe.deprecation_dumpster import (
|
||||
tests_UnitTestCase as UnitTestCase,
|
||||
)
|
||||
from frappe.deprecation_dumpster import (
|
||||
tests_utils_get_dependencies as get_dependencies,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ datetime_like_types = (datetime.datetime, datetime.date, datetime.time, datetime
|
|||
|
||||
__all__ = [
|
||||
"get_modules",
|
||||
"get_dependencies",
|
||||
"get_missing_records_doctypes",
|
||||
"get_missing_records_module_overrides",
|
||||
"make_test_records",
|
||||
"make_test_records_for_doctype",
|
||||
"make_test_objects",
|
||||
|
|
@ -36,8 +37,15 @@ def get_modules(doctype):
|
|||
|
||||
|
||||
@cache
|
||||
def get_dependencies(doctype):
|
||||
"""Get the dependencies for the specified doctype"""
|
||||
def get_missing_records_doctypes(doctype):
|
||||
"""Get the dependencies for the specified doctype in a depth-first manner"""
|
||||
# If already visited in a prior run
|
||||
if doctype in frappe.local.test_objects:
|
||||
return []
|
||||
else:
|
||||
# Infinite recrusion guard (depth-first discovery)
|
||||
frappe.local.test_objects[doctype] = []
|
||||
|
||||
module, test_module = get_modules(doctype)
|
||||
meta = frappe.get_meta(doctype)
|
||||
link_fields = meta.get_link_fields()
|
||||
|
|
@ -45,21 +53,73 @@ def get_dependencies(doctype):
|
|||
for df in meta.get_table_fields():
|
||||
link_fields.extend(frappe.get_meta(df.options).get_link_fields())
|
||||
|
||||
options_list = [df.options for df in link_fields]
|
||||
doctype_set = {df.options for df in link_fields if df.options != "[Select]"}
|
||||
|
||||
if hasattr(test_module, "test_dependencies"):
|
||||
options_list += test_module.test_dependencies
|
||||
to_add, to_remove = get_missing_records_module_overrides(test_module)
|
||||
doctype_set.update(to_add)
|
||||
doctype_set.difference_update(to_remove)
|
||||
|
||||
options_list = list(set(options_list))
|
||||
# Recursive depth-first traversal
|
||||
result = []
|
||||
for dep_doctype in doctype_set:
|
||||
result.extend(get_missing_records_doctypes(dep_doctype))
|
||||
|
||||
if hasattr(test_module, "test_ignore"):
|
||||
for doctype_name in test_module.test_ignore:
|
||||
if doctype_name in options_list:
|
||||
options_list.remove(doctype_name)
|
||||
result.append(doctype)
|
||||
return result
|
||||
|
||||
options_list.sort()
|
||||
|
||||
return options_list
|
||||
def get_missing_records_module_overrides(module) -> [set, set]:
|
||||
to_add = set()
|
||||
to_remove = set()
|
||||
if hasattr(module, "test_dependencies"):
|
||||
from frappe.deprecation_dumpster import deprecation_warning
|
||||
|
||||
deprecation_warning(
|
||||
"2024-10-09",
|
||||
"v17",
|
||||
"""test_dependencies was clarified to EXTRA_TEST_RECORD_DEPENDENCIES: run:
|
||||
```bash
|
||||
# Find Python files
|
||||
find . -name "*.py" | while read -r file; do
|
||||
# Check if the file contains 'test_dependencies' at the module level
|
||||
if grep -q "^test_dependencies" "$file"; then
|
||||
# Replace 'test_dependencies' with 'EXTRA_TEST_RECORD_DEPENDENCIES'
|
||||
sed -i 's/^test_dependencies/EXTRA_TEST_RECORD_DEPENDENCIES/' "$file"
|
||||
echo "Updated $file"
|
||||
fi
|
||||
done
|
||||
```""",
|
||||
)
|
||||
to_add.update(set(module.test_dependencies))
|
||||
|
||||
if hasattr(module, "EXTRA_TEST_RECORD_DEPENDENCIES"):
|
||||
to_add.update(set(module.EXTRA_TEST_RECORD_DEPENDENCIES))
|
||||
|
||||
if hasattr(module, "test_ignore"):
|
||||
from frappe.deprecation_dumpster import deprecation_warning
|
||||
|
||||
deprecation_warning(
|
||||
"2024-10-09",
|
||||
"v17",
|
||||
"""test_ignore was clarified to IGNORE_TEST_RECORD_DEPENDENCIES: run:
|
||||
```bash
|
||||
# Find Python files
|
||||
find . -name "*.py" | while read -r file; do
|
||||
# Check if the file contains 'test_dependencies' at the module level
|
||||
if grep -q "^test_ignore" "$file"; then
|
||||
# Replace 'test_ignore' with 'IGNORE_TEST_RECORD_DEPENDENCIES'
|
||||
sed -i 's/^test_ignore/IGNORE_TEST_RECORD_DEPENDENCIES/' "$file"
|
||||
echo "Updated $file"
|
||||
fi
|
||||
done
|
||||
```""",
|
||||
)
|
||||
to_remove.difference_update(set(module.test_ignore))
|
||||
|
||||
if hasattr(module, "IGNORE_TEST_RECORD_DEPENDENCIES"):
|
||||
to_remove.difference_update(set(module.IGNORE_TEST_RECORD_DEPENDENCIES))
|
||||
|
||||
return to_add, to_remove
|
||||
|
||||
|
||||
# Test record generation
|
||||
|
|
@ -70,7 +130,7 @@ def make_test_records(doctype, force=False, commit=False):
|
|||
|
||||
|
||||
def make_test_records_for_doctype(doctype, force=False, commit=False):
|
||||
return list(_make_test_records_for_doctype(doctype, force, commit))
|
||||
return list(_make_test_record(doctype, force, commit))
|
||||
|
||||
|
||||
def make_test_objects(doctype, test_records=None, reset=False, commit=False):
|
||||
|
|
@ -79,31 +139,12 @@ def make_test_objects(doctype, test_records=None, reset=False, commit=False):
|
|||
|
||||
def _make_test_records(doctype, force=False, commit=False):
|
||||
"""Make test records for the specified doctype"""
|
||||
|
||||
loadme = False
|
||||
|
||||
if doctype not in frappe.local.test_objects:
|
||||
loadme = True
|
||||
frappe.local.test_objects[doctype] = [] # infinite recursion guard, here
|
||||
|
||||
# First, create test records for dependencies
|
||||
for dependency in get_dependencies(doctype):
|
||||
if dependency != "[Select]" and dependency not in frappe.local.test_objects:
|
||||
yield from _make_test_records(dependency, force, commit)
|
||||
|
||||
# Then, create test records for the doctype itself
|
||||
if loadme:
|
||||
# Yield the doctype and record length
|
||||
yield (
|
||||
doctype,
|
||||
len(
|
||||
# Create all test records
|
||||
list(_make_test_records_for_doctype(doctype, force, commit))
|
||||
),
|
||||
)
|
||||
for _doctype in get_missing_records_doctypes(doctype):
|
||||
# Create all test records and yield
|
||||
yield (_doctype, len(list(_make_test_record(_doctype, force, commit))))
|
||||
|
||||
|
||||
def _make_test_records_for_doctype(doctype, force=False, commit=False):
|
||||
def _make_test_record(doctype, force=False, commit=False):
|
||||
"""Make test records for the specified doctype"""
|
||||
|
||||
test_record_log_instance = TestRecordLog()
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from frappe.website.serve import get_response
|
|||
from frappe.website.utils import clear_website_cache
|
||||
from frappe.website.website_generator import WebsiteGenerator
|
||||
|
||||
test_dependencies = ["Blog Post"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Blog Post"]
|
||||
|
||||
|
||||
class UnitTestBlogPost(UnitTestCase):
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from frappe.utils import set_request
|
|||
from frappe.website.doctype.web_form.web_form import accept
|
||||
from frappe.website.serve import get_response_content
|
||||
|
||||
test_dependencies = ["Web Form"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Web Form"]
|
||||
|
||||
|
||||
class UnitTestWebForm(UnitTestCase):
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from frappe.tests import IntegrationTestCase, UnitTestCase
|
|||
from frappe.utils import set_request
|
||||
from frappe.website.serve import get_response
|
||||
|
||||
test_dependencies = ["Blog Post"]
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Blog Post"]
|
||||
|
||||
|
||||
class UnitTestWebsiteRouteMeta(UnitTestCase):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue