Repository: wolever/parameterized

Issue:
expand doesn't work with mock.patch.multiple
When using [mock.patch.multiple](https://docs.python.org/3/library/unittest.mock.html#patch-multiple) as a decorator (class or test method), the mocks it creates are passed in as keyword arguments to the test function.

Unfortunately, this is not compatible with `expand`, as the standalone functions that it generates (`parameterized.parameterized.param_as_standalone_func.standalone_func`) only take unnamed positional arguments.

The simplest fix seems to be to just add unnamed keyword arguments as well:

```
@wraps(func)
def standalone_func(*a, **k):
    return func(*(a + p.args), **p.kwargs, **k)
```

Discussion:

Patch:
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 8196da0..0c3894b 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -13,6 +13,8 @@
     * Allow str, bytes, and any non-iterable input to be passed to
       ``@parameterized`` without wrapping in a tuple
       (https://github.com/wolever/parameterized/pull/157)
+    * Fix class-level ``mock.patch.multiple``
+      (https://github.com/wolever/parameterized/issues/138; thanks @ArthurGW)
 
 0.8.1 (2021-01-09)
     * Add README and LICENSE to pypi sdist package
diff --git a/parameterized/parameterized.py b/parameterized/parameterized.py
index d94be9b..56dc535 100644
--- a/parameterized/parameterized.py
+++ b/parameterized/parameterized.py
@@ -612,12 +612,12 @@ class parameterized(object):
     def param_as_standalone_func(cls, p, func, name):
         if inspect.iscoroutinefunction(func):
             @wraps(func)
-            async def standalone_func(*a):
-                return await func(*(a + p.args), **p.kwargs)
+            async def standalone_func(*a, **kw):
+                return await func(*(a + p.args), **p.kwargs, **kw)
         else:
             @wraps(func)
-            def standalone_func(*a):
-                return func(*(a + p.args), **p.kwargs)
+            def standalone_func(*a, **kw):
+                return func(*(a + p.args), **p.kwargs, **kw)
 
         standalone_func.__name__ = name
 


Test patch:
diff --git a/parameterized/test.py b/parameterized/test.py
index f124182..6c71f79 100644
--- a/parameterized/test.py
+++ b/parameterized/test.py
@@ -291,21 +291,29 @@ def test_mock_patch_standalone_function(foo, mock_umask):
         )
     )
 
-
+@mock.patch.multiple("os", umask=mock.DEFAULT)
 class TestParameterizedExpandWithMockPatchMultiple(TestCase):
     expect([
-        "test_mock_patch_multiple_expand(42, 'umask', 'getpid')",
+        "test_mock_patch_multiple_expand_on_method(42, 'umask', 'getpid')",
+        "test_mock_patch_multiple_expand_on_class(16, 'umask')",
     ])
 
     @parameterized.expand([(42, )])
-    @mock.patch.multiple("os", umask=mock.DEFAULT, getpid=mock.DEFAULT)
-    def test_mock_patch_multiple_expand(self, param, umask, getpid):
+    @mock.patch.multiple("os", getpid=mock.DEFAULT)
+    def test_mock_patch_multiple_expand_on_method(self, param, umask, getpid):
         missing_tests.remove(
-            "test_mock_patch_multiple_expand(%r, %r, %r)" %(
+            "test_mock_patch_multiple_expand_on_method(%r, %r, %r)" %(
                 param, umask._mock_name, getpid._mock_name
             )
         )
 
+    @parameterized.expand([(16, )])
+    def test_mock_patch_multiple_expand_on_class(self, param, umask):
+        missing_tests.remove(
+            "test_mock_patch_multiple_expand_on_class(%r, %r)" %(
+                param, umask._mock_name,
+            )
+        )
 
 expect("standalone", [
     "test_mock_patch_multiple_standalone(42, 'umask', 'getpid')",
@@ -684,4 +692,4 @@ if sys.version_info.major == 3 and sys.version_info.minor >= 8:
         @mock.patch("os.umask")
         async def test_one_async_function_patch_decorator(self, foo, mock_umask):
             missing_tests.remove("test_one_async_function_patch_decorator(%r, %r)" %
-                                 (foo, mock_umask._mock_name))
\ No newline at end of file
+                                 (foo, mock_umask._mock_name))