Repository: frappe/frappe

Issue:
Typing validations should be ignored for tests
## Description of the issue
https://github.com/frappe/frappe/blob/010aa4636ace30a9df4c09f0ca991169f34274b9/frappe/utils/typing_validations.py#L164

If you're writing Frappe tests using the `unittest.mock` module, there might be cases where the argument object is replaced with a `Mock` or `MagicMock` object. This breaks typing validations when running CI tests using the `develop` branch.

I think a reasonable approach could be to either ignore all validations during tests, and/or allow configuring this behaviour per-test (with the default being "ignore").

## Context

**Output of `bench version`**
```
frappe 14.14.2
```



Discussion:
@Alchez is it enough if we just add exemption for unittest mocks? 


Testing this in tests is useful to ensure sanity as not everything might be tested in UI tests.
@ankush, yeah I think that should be good enough too. That list could be expanded if someone else is using something like `pytest` for mocking, but a good start.

Resolving patch:
diff --git a/frappe/utils/typing_validations.py b/frappe/utils/typing_validations.py
--- a/frappe/utils/typing_validations.py
+++ b/frappe/utils/typing_validations.py
@@ -3,6 +3,7 @@
 from inspect import _empty, isclass, signature
 from types import EllipsisType
 from typing import ForwardRef, TypeVar, Union
+from unittest import mock
 
 from pydantic import ConfigDict
 
@@ -77,8 +78,8 @@ def transform_parameter_types(func: Callable, args: tuple, kwargs: dict):
 	"""
 	Validate the types of the arguments passed to a function with the type annotations
 	defined on the function.
-
 	"""
+
 	if not (args or kwargs) or not func.__annotations__:
 		return args, kwargs
 
@@ -117,6 +118,9 @@ def transform_parameter_types(func: Callable, args: tuple, kwargs: dict):
 			continue
 		elif any(isinstance(x, ForwardRef | str) for x in getattr(current_arg_type, "__args__", [])):
 			continue
+		# ignore unittest.mock objects
+		elif isinstance(current_arg_value, mock.Mock):
+			continue
 
 		# allow slack for Frappe types
 		if current_arg_type in SLACK_DICT:


Test patch:
diff --git a/frappe/tests/test_utils.py b/frappe/tests/test_utils.py
--- a/frappe/tests/test_utils.py
+++ b/frappe/tests/test_utils.py
@@ -1263,6 +1263,8 @@ def test_cint(self, floating_point, integer):
 
 class TestArgumentTypingValidations(FrappeTestCase):
 	def test_validate_argument_types(self):
+		from unittest.mock import AsyncMock, MagicMock, Mock
+
 		from frappe.core.doctype.doctype.doctype import DocType
 		from frappe.utils.typing_validations import (
 			FrappeTypeError,
@@ -1281,6 +1283,10 @@ def test_sequence(a: str, b: list[dict] | None = None, c: dict[str, int] | None
 		def test_doctypes(a: DocType | dict):
 			return a
 
+		@validate_argument_types
+		def test_mocks(a: str):
+			return a
+
 		self.assertEqual(test_simple_types(True, 2.0, True), (1, 2.0, True))
 		self.assertEqual(test_simple_types(1, 2, 1), (1, 2.0, True))
 		self.assertEqual(test_simple_types(1.0, 2, 1), (1, 2.0, True))
@@ -1304,6 +1310,13 @@ def test_doctypes(a: DocType | dict):
 		with self.assertRaises(FrappeTypeError):
 			test_doctypes("a")
 
+		self.assertEqual(test_mocks("Hello World"), "Hello World")
+		for obj in (AsyncMock, MagicMock, Mock):
+			obj_instance = obj()
+			self.assertEqual(test_mocks(obj_instance), obj_instance)
+		with self.assertRaises(FrappeTypeError):
+			test_mocks(1)
+
 
 class TestChangeLog(FrappeTestCase):
 	def test_check_release_on_github(self):