模拟接受 **kwargs 的函数
mocking a function that accepts **kwargs
我在模拟接受 **kwargs 的函数时遇到问题。场景是我有 ClassA(在我的特定情况下是我没有写的东西)并且它有一个带有 **kwargs 的函数。 ClassB 有一个 ClassA 的实例,并调用 **kwargs 函数。我想通过模拟调用 Class A 的函数来测试 ClassB。
这是我到目前为止所尝试的方法,在我的两次尝试中,我都以 TypeError 告终。有什么办法吗?我是否应该重新考虑这个问题的另一个方面?
import unittest
#a class i have no control over. Has a function accepting **kwargs
class ClassA(object):
def classFunctionAcceptingKwargs(self, **kwargs):
return kwargs["a"] + kwargs["b"]
# a mock of the above class
class Mock_ClassA(object):
def __init__(self):
self.mockclassFunctionAcceptingKwargs = lambda **kwargs: None
def classFunctionAcceptingKwargs(self, **kwargs):
#FAILS: TypeError: mockFunctionAcceptingKwargs() takes exactly 0 arguments (1 given)
return self.mockclassFunctionAcceptingKwargs(kwargs)
#ALSO FAILS: TypeError: mockFunctionAcceptingKwargs() argument after ** must be a mapping, not set
#return self.mockclassFunctionAcceptingKwargs(**{kwargs["a"] + kwargs["b"]})
#class B calls the class A kwargs but exposes a function with a dict
class ClassB(object):
def __init__(self, classA):
self.classA = classA
def doSomething(self, dict):
return self.classA.classFunctionAcceptingKwargs(**dict)
class TestClassA(unittest.TestCase):
def runTest(self):
a = ClassA()
result = a.classFunctionAcceptingKwargs(**{"a":1, "b": 2})
self.assertEqual(result, 3)
class TestClassB(unittest.TestCase):
def runTest(self):
mock = Mock_ClassA()
def mockFunctionAcceptingKwargs(**kwargs):
self.assertEqual(kwargs["a"], 1)
self.assertEqual(kwargs["b"], 2)
mock.mockclassFunctionAcceptingKwargs = mockFunctionAcceptingKwargs
b = ClassB(mock)
b.doSomething({"a": 1, "b": 2})
堆栈跟踪:
Test Name: TestClassB
Test Outcome: Failed
Result StandardError:
======================================================================
ERROR: runTest (module1.TestClassB)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\test\module1.py", line 49, in runTest
b.doSomething({"a": 1, "b": 2})
File "C:\test\module1.py", line 28, in doSomething
return self.classA.classFunctionAcceptingKwargs(**dict)
File "C:\test\module1.py", line 18, in classFunctionAcceptingKwargs
return self.mockclassFunctionAcceptingKwargs(kwargs)
TypeError: mockFunctionAcceptingKwargs() takes exactly 0 arguments (1 given)
----------------------------------------------------------------------
Ran 1 test in 12.873s
FAILED (errors=1)
我不确定你为什么要调用另一个函数。但是如果你 必须 有一个实例属性 mockclassFunctionAcceptingKwargs
函数,那么只需传递 kwargs
字典 **kwargs
:
class Mock_ClassA(object):
# ...
def classFunctionAcceptingKwargs(self, **kwargs):
return self.mockclassFunctionAcceptingKwargs(**kwargs)
如果你只需要 classFunctionAcceptingKwargs
存在,你根本不需要调用那个 lambda:
class Mock_ClassA(object):
def __init__(self, mock_result):
self.mock_result = mock_result
def classFunctionAcceptingKwargs(self, **kwargs):
self.called_with = kwargs
return self.mock_result
然后只需传入模拟的 return 值,无论您需要传回给 ClassB
进行测试,之后您都可以检查是否也传入了正确的值:
mock = Mock_ClassA(3) # to return 3 back to the caller
b = ClassB(mock)
b.doSomething({"a": 1, "b": 2})
self.assertEqual(mock.called_with, {'a': 1, 'b': 2})
您可能要使用unittest.mock
library to build a mock object to pass in (available in Python 3, and a backport for Python 2 可以安装)。它会让你创建一个模拟的 ClassA
然后使用 API 来测试模拟是否以预期的方式使用:
try:
# Python 3
from unittest import mock
except ImportError:
# Python 2, backport
import mock
class TestClassB(unittest.TestCase):
def runTest(self):
mockA = mock.Mock(spec=ClassA) # only accept attributes ClassA also has
mockA.classFunctionAcceptingKwargs.return_value = 3 # or whatever else you want it to return
b = ClassB(mockA)
b.doSomething({"a": 1, "b": 2})
mockA.classFunctionAcceptingKwargs.assert_called_once_with(a=1, b=2)
使用 unittest.mock
作为模拟层的演示:
>>> from unittest import mock
>>> class ClassA(object):
... def classFunctionAcceptingKwargs(self, **kwargs):
... return kwargs["a"] + kwargs["b"]
...
>>> class ClassB(object):
... def __init__(self, classA):
... self.classA = classA
... def doSomething(self, dict):
... return self.classA.classFunctionAcceptingKwargs(**dict)
...
>>> mockA = mock.Mock(spec=ClassA)
>>> mockA.classFunctionAcceptingKwargs.return_value = 3
>>> b = ClassB(mockA)
>>> b.doSomething({"a": 1, "b": 2})
3
>>> mockA.classFunctionAcceptingKwargs.assert_called_once_with(a=1, b=2) # passes, no exception raised
>>> mockA.mock_calls
[call.classFunctionAcceptingKwargs(a=1, b=2)]
我在模拟接受 **kwargs 的函数时遇到问题。场景是我有 ClassA(在我的特定情况下是我没有写的东西)并且它有一个带有 **kwargs 的函数。 ClassB 有一个 ClassA 的实例,并调用 **kwargs 函数。我想通过模拟调用 Class A 的函数来测试 ClassB。
这是我到目前为止所尝试的方法,在我的两次尝试中,我都以 TypeError 告终。有什么办法吗?我是否应该重新考虑这个问题的另一个方面?
import unittest
#a class i have no control over. Has a function accepting **kwargs
class ClassA(object):
def classFunctionAcceptingKwargs(self, **kwargs):
return kwargs["a"] + kwargs["b"]
# a mock of the above class
class Mock_ClassA(object):
def __init__(self):
self.mockclassFunctionAcceptingKwargs = lambda **kwargs: None
def classFunctionAcceptingKwargs(self, **kwargs):
#FAILS: TypeError: mockFunctionAcceptingKwargs() takes exactly 0 arguments (1 given)
return self.mockclassFunctionAcceptingKwargs(kwargs)
#ALSO FAILS: TypeError: mockFunctionAcceptingKwargs() argument after ** must be a mapping, not set
#return self.mockclassFunctionAcceptingKwargs(**{kwargs["a"] + kwargs["b"]})
#class B calls the class A kwargs but exposes a function with a dict
class ClassB(object):
def __init__(self, classA):
self.classA = classA
def doSomething(self, dict):
return self.classA.classFunctionAcceptingKwargs(**dict)
class TestClassA(unittest.TestCase):
def runTest(self):
a = ClassA()
result = a.classFunctionAcceptingKwargs(**{"a":1, "b": 2})
self.assertEqual(result, 3)
class TestClassB(unittest.TestCase):
def runTest(self):
mock = Mock_ClassA()
def mockFunctionAcceptingKwargs(**kwargs):
self.assertEqual(kwargs["a"], 1)
self.assertEqual(kwargs["b"], 2)
mock.mockclassFunctionAcceptingKwargs = mockFunctionAcceptingKwargs
b = ClassB(mock)
b.doSomething({"a": 1, "b": 2})
堆栈跟踪:
Test Name: TestClassB
Test Outcome: Failed
Result StandardError:
======================================================================
ERROR: runTest (module1.TestClassB)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\test\module1.py", line 49, in runTest
b.doSomething({"a": 1, "b": 2})
File "C:\test\module1.py", line 28, in doSomething
return self.classA.classFunctionAcceptingKwargs(**dict)
File "C:\test\module1.py", line 18, in classFunctionAcceptingKwargs
return self.mockclassFunctionAcceptingKwargs(kwargs)
TypeError: mockFunctionAcceptingKwargs() takes exactly 0 arguments (1 given)
----------------------------------------------------------------------
Ran 1 test in 12.873s
FAILED (errors=1)
我不确定你为什么要调用另一个函数。但是如果你 必须 有一个实例属性 mockclassFunctionAcceptingKwargs
函数,那么只需传递 kwargs
字典 **kwargs
:
class Mock_ClassA(object):
# ...
def classFunctionAcceptingKwargs(self, **kwargs):
return self.mockclassFunctionAcceptingKwargs(**kwargs)
如果你只需要 classFunctionAcceptingKwargs
存在,你根本不需要调用那个 lambda:
class Mock_ClassA(object):
def __init__(self, mock_result):
self.mock_result = mock_result
def classFunctionAcceptingKwargs(self, **kwargs):
self.called_with = kwargs
return self.mock_result
然后只需传入模拟的 return 值,无论您需要传回给 ClassB
进行测试,之后您都可以检查是否也传入了正确的值:
mock = Mock_ClassA(3) # to return 3 back to the caller
b = ClassB(mock)
b.doSomething({"a": 1, "b": 2})
self.assertEqual(mock.called_with, {'a': 1, 'b': 2})
您可能要使用unittest.mock
library to build a mock object to pass in (available in Python 3, and a backport for Python 2 可以安装)。它会让你创建一个模拟的 ClassA
然后使用 API 来测试模拟是否以预期的方式使用:
try:
# Python 3
from unittest import mock
except ImportError:
# Python 2, backport
import mock
class TestClassB(unittest.TestCase):
def runTest(self):
mockA = mock.Mock(spec=ClassA) # only accept attributes ClassA also has
mockA.classFunctionAcceptingKwargs.return_value = 3 # or whatever else you want it to return
b = ClassB(mockA)
b.doSomething({"a": 1, "b": 2})
mockA.classFunctionAcceptingKwargs.assert_called_once_with(a=1, b=2)
使用 unittest.mock
作为模拟层的演示:
>>> from unittest import mock
>>> class ClassA(object):
... def classFunctionAcceptingKwargs(self, **kwargs):
... return kwargs["a"] + kwargs["b"]
...
>>> class ClassB(object):
... def __init__(self, classA):
... self.classA = classA
... def doSomething(self, dict):
... return self.classA.classFunctionAcceptingKwargs(**dict)
...
>>> mockA = mock.Mock(spec=ClassA)
>>> mockA.classFunctionAcceptingKwargs.return_value = 3
>>> b = ClassB(mockA)
>>> b.doSomething({"a": 1, "b": 2})
3
>>> mockA.classFunctionAcceptingKwargs.assert_called_once_with(a=1, b=2) # passes, no exception raised
>>> mockA.mock_calls
[call.classFunctionAcceptingKwargs(a=1, b=2)]