I have the following structure:
sources/
- parser/
- sources_parser.py # SourcesParser class is here
- tests/
- test_sources_parsers.py # SourcesParserTest is here
sources_parser.py:
from sources.parser.sitemap_parser import SitemapParser
class SourcesParser(object):
__sitemap_parser: SitemapParser
def __init__(self) -> None:
super().__init__()
self.__sitemap_parser = SitemapParser()
def parse(self):
# ...
urls = self.__parse(source)
self.logger.info(f'Got {len(urls)} URLs')
def __parse(self, source: NewsSource) -> List[str]:
results = self.__sitemap_parser.parse_sitemap(source.url)
self.logger.info(f'Results: {results}, is list: {isinstance(results, list)}')
return results
Test:
class SourcesParserTest(TestCase):
#patch('sources.parser.sources_parser.SitemapParser')
def test_normal_parse(self, mock_sitemap_parser):
mock_sitemap_parser.parse_sitemap.return_value = [
'https://localhost/news/1',
'https://localhost/news/2',
'https://localhost/news/3',
]
parser = SourcesParser()
parser.parse()
And the logs are:
Results: <MagicMock name='SitemapParser().parse_sitemap()' id='5105954240'>, is list: False
Got 0 URLs
If I understand mocking correctly, the parse_sitemap call should return the given list of URLs, but instead it returns a Mock object which is converted to an empty list.
Python version is 3.9.
What's wrong?
If mocking a member function, you have to mock the function on the mocked instance object, not the function on the mocked class object. This may not be completely intuitive, because a member function belongs to a class, but you may think about it as the bound versus unbound method - you have to mock the bound method.
With Mock, mocking an instance is done by using return_value on the class object, similar to mocking the result of a function call, so in your case you need:
#patch('sources.parser.sources_parser.SitemapParser')
def test_normal_parse(self, mock_sitemap_parser):
mocked_parser = mock_sitemap_parser.return_value # mocked instance
mock_parser.parse_sitemap.return_value = [
'https://localhost/news/1',
'https://localhost/news/2',
'https://localhost/news/3',
]
...
(split the mock for illustration only)
Related
I'm learning python design patterns from github repo faif/python-patterns and found the example chain_of_responsibility implements abstractmethod check_range as staticmethod.
My question is, is there any benefit other than less typing a self?
Simplify code is
from abc import ABC, abstractmethod
class A(ABC):
#abstractmethod
def foo(self, x):
pass
class B(A):
#staticmethod
def foo(x):
print("B.foo", x)
# both the two works
B.foo(1)
b = B()
b.foo(2)
There's no particular benefit. Sure, not all the check_range methods need an instance of the handler class that defines it, so they are declared as static methods.
But there's no reason any of the classes in your linked page need to exist in the first place, because in Python you can just store a function itself in a list, rather than storing some other dummy object that has an equivalent method.
Here's how you can implement "chain of responsibility" in Python idiomatically.
def check_range0(request):
return request in range(10)
# A closure can be used in place of a class
def make_check_range1():
start = 10
stop = 20
return lambda request: request in range(start, stop)
# Another way of using a closure in place of a class
def make_check_range2(start, stop):
return lambda request: request in range(start, stop)
def fallback(request):
print("No handler for {request}")
handlers = [check_range0, make_check_range1(), make_check_range2(20, 30)]
for request in requests:
if any((handler:=f)(request) for f in handlers):
print(f"{request} handled by {handler.__name__}")
else:
fallback(request)
A simple list takes the place of the linked list implied by Handler. Each subclass of Handler is replaced by a regular function (or a function that returns a closure, just to emphasize that a class is not necessary to provide or store state). The any function implements the iteration provided by Handler.handle.
If you really want a handler class, you can define it more simply than the example.
class Handler(ABC):
def __init__(self, handlers=None):
if handlers is None:
handlers = []
self.handlers = handlers
self.fallback = lambda request: pass
def add_handler(self, f):
self.handlers.append(f)
# Barely necessary; you can set the fallback
# attribute on a Handler instance yourself.
def set_fallback(self, f):
self.fallback = f
def handle(self, request):
# An alternative to any()
for h in self.handlers:
if h(request):
break
else:
self.fallback(request)
h = Handler([check_range0])
h.add_handler(make_check_range1())
h.add_handler(make_check_range2(20, 30))
def fallback(request):
print(f"No handler for {request}"
h.fallback = fallback
# h.set_fallback(fallback)
for request in requests:
h.handle(request)
I have a test function which is getting a mocked class via a #mock.patch decorator. Within the function, I want to force a return value of one of the method (which is needed for the test to succeed) as well as make assertions on another of the methods; these methods are exercised by calling a free function in the class's module.
Example module bla.py:
class Tool:
def __init__(self):
self.items = list()
def m1(self, val):
self.items.append(val)
def act(self):
return len(self.items)
def exercise_tool():
toolio = Tool()
for i in range(2):
toolio.m1(i)
return toolio.act() <= 2
and its test:
from unittest import mock
import bla
#mock.patch("bla.Tool")
def test_exercise_tool(mtool):
mtool.act.return_value = 3
assert not bla.exercise_tool() # verify the mocked return value
mtool.m1.assert_was_called()
The test fails with TypeError: '<=' not supported between instances of 'MagicMock' and 'int'.
So how do I get this test to pass?
I'm writing unit tests for the following the following class.
module1/node_base.py
#dataclass
class NodeBase(metaclass=ABCMeta):
account: AccountBase
name: str
ec2_id: str = ''
def start(self):
self.account.session.resource('ec2').instances.filter(
InstanceIds=[self.ec2_id]).start()
self.account.session is AWS session. How to write the test function? (to check if filter() is called with parameter InstanceIds=[self.ec2_id] and start() is called?)
test_start.py
from module1.node_base import NodeBase
class Node(NodeBase):
'''Node'''
#pytest.fixture
def sut() -> Node:
session = Mock()
def get_resource():
instances = Mock()
def get_filter(filters):
def get_start():
pass
return get_start
instances.filter = get_filter
return instances
session.resource = get_resource()
account = Mock()
account.session = session
return Node(account=account, name='Test')
def test_start(sut):
sut.start()
assert sut.account.session.resource('ec2').instances.filter.call_count == 1
assert sut.account.session.resource('ec2').instances.filter().start.call_count == 1
It seems very verbose to setup the fixture. Is it a simpler approach to test it?
How to test if filter() is called with a particular parameter ['some_id']?
You can use patch to assert whether it is being called with a particular parameter. For example:
#patch('path.to.filter.function')
def func1(mock_filter): // Test case for filter function
id = '1234'
parameter = '1234'
filter(id) // Calling the function here
mock_filter.assert_called_with(parameter)
Here, for the example I'm calling the filter function with value '1234', and later checking if it was called with the same value('1234') or not.
It will return true if the function is called with the parameter you have defined.
I've read a lot of information online about mocking entire classes. However, I have a class with multiple methods; for example:
class A():
def methoda(param1, param2):
do things
return thing
def methodb(param3):
do things (including something calling methoda)
How can I mock out methoda in a testing file to return a desired value so that I can test methodb? I don't want to mock out the entire class.
What I've tried doing:
from mock import patch, mock, MagicMock
from sourceA.models import ClassName
from django.test import TestCase
class ClassTest(TestCase):
#patch('sourceA.models.ClassName.methodA')
def test_method(self, mock_method_return):
mock_method_return.return_value = 10
instance = ClassName()
instance.methodB #METHOD B CALLS METHOD A; I want method A to return 10
print "OUTPUT", instance.FIELDA #Debug Tool - Field A is modified by Method B, based on the return value of method A
assert instance.FIELDA == 10
I have also tried this:
class ClassTest(TestCase):
#patch('sourceA.models.ClassName.methodA', return_value=10)
def test_method(self, mock_method_return):
instance = Mock(spec=ClassName())
instance.methodB #METHOD B CALLS METHOD A; I want method A to return 10
------ OR instance methodB = MagicMock(return_value=10) and then instance.MethodB
print "OUTPUT", instance.FIELDA #Debug Tool - Field A is modified by Method B, based on the return value of method A
self.assertEqual(instance.FIELDA, 10)
I'm not sure what else I should try/what I am doing wrong.
I am curious how to create a custom method for a class at runtime...
I mean for example with name of method, name of parameters, body of method read from database and assign this method to a class or to an instance.
I have a found possibility to add method that is already written:
class A:
def __init__(self):
pass
def method(self):
return True
A.method = method
a = A()
print(a.method())
but I am interested in completely assembling a new method from scratch:
name = "method"
params = ["self"] # Params in list should be a strings
body = "return True"
# To create method from pieces
Is it possible using __dict__ ? Or how else this be done?
Methods are another attribute on the object that is the class. They can be added like other attributes:
Code:
class A:
def __init__(self):
pass
def method(self):
return True
def another_method(self):
return False
setattr(A, 'another_method', another_method)
Test Code:
a = A()
print(a.another_method())
Results:
False
Methods from a string:
Add if you really need to get your methods from a database or such you can use exec like:
method_string = """
def yet_another_method(self):
return type(self).__name__
"""
exec(method_string)
setattr(A, 'yet_another_method', yet_another_method)
a = A()
print(a.yet_another_method())
Results:
A
This answer has to be treated with care, using exec or eval can run arbitary code and may compromise your system. So if you rely on user-input to create the function you mustn't use this!!!
The warning aside you can simply create anything using exec:
exec("""
def method():
return True
""")
>>> method()
True
So what you basically need is just a way to get your requirements in there:
functionname = 'funfunc'
parameters = ['a', 'b']
body = 'return a + b'
exec("""
def {functionname}({parameters}):
{body}
""".format(
functionname=functionname,
parameters=', '.join(parameters),
body='\n'.join([' {line}'.format(line=line) for line in body.split('\n')])))
The body will be indented so that it's valid syntax and the parameter list will be joined using ,. And the test:
>>> funfunc(1, 2)
3
One of the best solutions that I have found is the following:
def import_code(code, name, add_to_sys_modules=0):
"""
Import dynamically generated code as a module. code is the
object containing the code (a string, a file handle or an
actual compiled code object, same types as accepted by an
exec statement). The name is the name to give to the module,
and the final argument says wheter to add it to sys.modules
or not. If it is added, a subsequent import statement using
name will return this module. If it is not added to sys.modules
import will try to load it in the normal fashion.
import foo
is equivalent to
foofile = open("/path/to/foo.py")
foo = importCode(foofile,"foo",1)
Returns a newly generated module.
"""
import sys,imp
module = imp.new_module(name)
exec(code,module.__dict__)
if add_to_sys_modules:
sys.modules[name] = module
return module
class A:
def __init__(self):
pass
name = "method"
params = ["self"] # Params in list should be a strings
body = "return True"
scratch = "def {0}({1}):\n\t{2}".format(name, ','.join(params), body)
new_module = import_code(scratch, "test")
A.method = new_module.method
a = A()
print(a.method())
Original function import_code by the following link http://code.activestate.com/recipes/82234-importing-a-dynamically-generated-module/
Using this solution I can dynamically create methods, load them in runtime and link to whatever I want object !!