Invoked method from Python unit test issues - python

I'm trying to invoke the following method, post_sample (filename index.py):
class Main(Resource):
#app.route('/sample', methods=['POST'])
def post_sample():
return 'POST received'
From the following unittest code:
def test_post():
Main().post_sample()
print 'test_here'
But when I run I get the following error, which I'm having trouble making sense of:
> Main().post_sample()
E TypeError: post_sample() takes no arguments (1 given)
tests/test_post.py:8: TypeError
TIA!

post_sample is an instance method and as such must accept at minimum one argument, a reference to the instance:
def post_sample(self):

Related

Mock Patch raising error TypeError: __init__() takes 1 positional argument but 2 were given

I am trying to unittest my code, and am using a mock function to raise an exception. For which I am using #patch decorator from the mock class, and using side_effect to raise the error. However, I am getting an error whose reason I am not able to figure out.
My Code:
# Test class to test the wiki_parser.
class TestParseWiki(unittest.TestCase):
def setUp(self) -> None:
return super().setUp()
def tearDown(self) -> None:
return super().tearDown()
#patch('parse_wiki.parse_wiki_info', return_value=(1808130, ['https://en.wikipedia.org/wiki/Lua_programming_language', 'https://en.wikipedia.org/wiki/Squirrel_programming_language']))
def test_valid_case(self, mock_parse_wiki_info):
valid_wiki_url = 'https://en.wikipedia.org/w/api.php?format=json&action=query&titles=SMALL&prop=revisions&rvprop=content'
self.assertEqual(
mock_parse_wiki_info(valid_wiki_url),
(1808130, ['https://en.wikipedia.org/wiki/Lua_programming_language', 'https://en.wikipedia.org/wiki/Squirrel_programming_language'])
)
#patch('parse_wiki.parse_wiki_info', side_effect=FailedRequestException('Failed to complete request. Kindly retry later.'))
def test_failed_request_case(self):
invalid_wiki_url = 'https://en.wikipedia.org/w/api.php?format=json&action=query&titles=SMALL&rvprop=content'
with self.assertRaises(FailedRequestException) as e:
parse_wiki_info(invalid_wiki_url)
self.assertEqual(
str(e.exception), "Failed to complete request. Kindly retry later."
)
I am getting the following error:
=============================================================================== ERRORS ================================================================================
_________________________________________________________________ ERROR collecting test_parse_wiki.py _________________________________________________________________
test_parse_wiki.py:7: in <module>
class TestParseWiki(unittest.TestCase):
test_parse_wiki.py:38: in TestParseWiki
#patch('parse_wiki.parse_wiki_info', FailedRequestException('Failed to complete request. Kindly retry later.'))
E TypeError: __init__() takes 1 positional argument but 2 were given
======================================================================= short test summary info =======================================================================
ERROR test_parse_wiki.py - TypeError: __init__() takes 1 positional argument but 2 were given
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
========================================================================== 1 error in 0.18s ===========================================================================
Kindly ignore the line numbers, I haven't added some test cases here.
How can I resolve this error?
With:
parse_wiki_info(invalid_wiki_url)
the variable parse_wiki_info holds a reference to the original parse_wiki.parse_wiki_info and does not get changed when parse_wiki.parse_wiki_info, an attribute of the parse_wiki module object, gets reassigned by patch to a Mock object.
Instead, you should import parse_wiki as a module object and reference parse_wiki_info as its attribute so that you can test the updated references to the Mock object:
import parse_wiki
...
with self.assertRaises(FailedRequestException) as e:
parse_wiki.parse_wiki_info(invalid_wiki_url)

Add extra steps to a function for testing purposes with monkeypatch

I have a class like the following
class MyMixin:
def base_dir(self):
return "/mydir"
def compute(self, origin, destination):
pass # execute some computation, not relevant
For testing purposes, I need to modify the base_dir. I used monkeypatch:
import MyMixin
#pytest.fixture
def global_computation(monkeypatch, temp_dir):
with monkeypatch.context() as mp:
mp.setattr(MyMixin, "base_dir", temp_dir)
yield mp
That works well, and I would like to mock the compute function with another function that basically extends the original with a few extra lines of code.
Ideally, it would be something like this
def enhance_compute()
result = compute()
extra_step()
return result
mp.setattr(MyMixin, "compute", enhance_compute)
However, I get an attribute error and I can't understand how to fix it.
Proceeding by steps, I tried to:
Re-assign the same function (works fine)
mp.setattr(MyMixin, "compute", MyMixin.compute)
Create another function and assign it instead (raises the error):
def compute(origin, destination):
return MyMixin.compute(origin, destination)
mp.setattr(MyMixin, "compute", compute)
The error is the following
<Result ConstructorTypeError("<function _manip_config at 0x7ff7c0cac550> raised an error: compute() got multiple values for argument 'destination'")>.exit_code
I'm messing up with the arguments, but I'm not sure how I should pass them correctly.

Decorator which skips `self` for methods

Let's say we have multiple functions which all accept an URL as their first argument and this URL needs to be validated. This can be nicely solved with a decorator
def validate_url(f):
def validated(url, *args, **kwargs):
assert len(url.split('.')) == 3 # trivial example
return f(url, *args, **kwargs)
return validated
#validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
pass
This approach will work and allow me to factor the validation behavior out of many instances of similar functions. But now I would want to write a class method which also takes a validated URL. However, the naive approach will not work
class SomeClass:
#validate_url
def some_method(self, url, some_other_args):
pass
because we will end up attempting to validate self and not url. My question is how to write a single decorator which will work for both functions and methods with the minimum amount of boilerplate.
Note 1: I am aware why this happens, it's just that I don't know how to work around this in the most elegant manner.
Note 2: The URL validation problem is just an example, so checking if isinstance(args[0], str) is not a good solution.
One solution would be to somehow detect whether the decorated function is a class method or not — which seems to be difficult if not impossible (as far as I can tell anyway) to do so cleanly. The inspect module's ismethod() and isfunction() don't work inside a decorator used inside a class definition.
Given that, here's a somewhat hacky way of doing it which checks to see if the decorated callable's first argument has been given the name "self", which is the coding convention for it in class methods (although it is not a requirement, so caveat emptor and use at your own risk).
The following code seems to work in both Python 2 and 3. However in Python 3 it may raise DeprecationWarnings depending on exactly what sub-version is being used—so they have been suppressed in a section of the code below.
from functools import wraps
import inspect
import warnings
def validate_url(f):
#wraps(f)
def validated(*args, **kwargs):
with warnings.catch_warnings():
# Suppress DeprecationWarnings in this section.
warnings.simplefilter('ignore', category=DeprecationWarning)
# If "f"'s first argument is named "self",
# assume it's a method.
if inspect.getargspec(f).args[0] == 'self':
url = args[1]
else: # Otherwise assume "f" is a ordinary function.
url = args[0]
print('testing url: {!r}'.format(url))
assert len(url.split('.')) == 3 # Trivial "validation".
return f(*args, **kwargs)
return validated
#validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
print('some_func() called')
class SomeClass:
#validate_url
def some_method(self, url, some_other_args):
print('some_method() called')
if __name__ == '__main__':
print('** Testing decorated function **')
some_func('xxx.yyy.zzz', 'another arg')
print(' URL OK')
try:
some_func('https://bogus_url.com', 'another thing')
except AssertionError:
print(' INVALID URL!')
print('\n** Testing decorated method **')
instance = SomeClass()
instance.some_method('aaa.bbb.ccc', 'something else') # -> AssertionError
print(' URL OK')
try:
instance.some_method('foo.bar', 'arg 2') # -> AssertionError
except AssertionError:
print(' INVALID URL!')
Output:
** Testing decorated function **
testing url: 'xxx.yyy.zzz'
some_func() called
URL OK
testing url: 'https://bogus_url.com'
INVALID URL!
** Testing decorated method **
testing url: 'aaa.bbb.ccc'
some_method() called
URL OK
testing url: 'foo.bar'
INVALID URL!

Simple class in Python error

I get an error message saying something like:
TypeError: readFromFile() takes exactly 2 arguments (1 given)
This is my very first class in Python btw.
from numpy import *
from pycalfem import *
from pycalfem_utils import *
class FlowModel:
def __init__(self):
self.readFromFile()
def readFromFile(self,filename):
Inputfile=open(filename,'r')
ep=readFloat(Inputfile)
print(str(ep))
Inputfile.close()
if __name__=="__main__":
flowmodel=FlowModel()
flowmodel.readFromFile("indata.txt")
You call self.readFromFile() without arguments, when you create the FlowModel instance:
def __init__(self):
self.readFromFile()
This means that the method is only passed self, the first argument, but not the second argument filename.
Remove that line; you don't have a filename to pass in there. You already are using that method correctly outside of the __init__ method after creating the instance.

proper way python mock __init__() method that returns a fake class

Trying to mock out calls to pyazure library for django testing, but I can't figure out how to mock out the PyAzure class constructor so that it doesn't cause a TypeError. Is there a better way to approach mocking out an access library that generates a connection object?
Anything I've tried other than None generates a TypeError, which means I can't really even begin to test any of the PyAzure connection methods with actual return values. What is the best way to replace a working class with a fake class using mock?
Test Error:
======================================================================
ERROR: test_management_certificate_connect (azure_cloud.tests.ViewsTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/bschott/Source/django-nimbis/apps/azure_cloud/tests.py", line 107, in test_management_certificate_connect
self.cert1.connect()
File "/Users/bschott/Source/django-nimbis/apps/azure_cloud/models.py", line 242, in connect
subscription_id=self.subscription.subscription_id)
TypeError: __init__() should return None, not 'FakeAzure'
----------------------------------------------------------------------
tests.py:
class ViewsTest(TestCase):
def setUp(self):
...
self.cert1 = ManagementCertificate.objects.create(
name="cert1",
subscription=self.subscription1,
management_cert=File(open(__file__), "cert1.pem"),
owner=self.user1)
...
class FakeAzure(object):
""" testing class for azure """
def list_services(self):
return ['service1', 'service2', 'service3']
def list_storages(self):
return ['storage1', 'storage2', 'storage3']
#mock.patch.object(pyazure.PyAzure, '__init__')
def test_management_certificate_connect(self, mock_pyazure_init):
mock_pyazure_init.return_value = self.FakeAzure()
self.cert1.connect()
assert mock_pyazure_init.called
models.py
class ManagementCertificate(models.Model):
# support connection caching to azure
_cached_connection = None
def connect(self):
"""
Connect to the management interface using these credentials.
"""
if not self._cached_connection:
self._cached_connection = pyazure.PyAzure(
management_cert_path=self.management_cert.path,
subscription_id=self.subscription.subscription_id)
logging.debug(self._cached_connection)
return self._cached_connection
You seem to have a misconception about what __init__() does. Its purpose is to initialise an instance that was already created earlier. The first argument to __init__() is self, which is the instance, so you can see it was already allocated when __init__() is called.
There is a method __new__() that is called before __init__() to create the actual instance. I think it would be much easier, though, to replace the whole class by a mock class, instead of mocking single methods.

Categories