Same mock object has different ids in test class and testable class - python-3.x

I want to write unit tests for the main.py class. The file structure of my project is like this,
my unit tests are included in test/source/code_files folder. I want to mock some methods in the main class too. (which uses variables in source/config/config.py) I'm using the patch for this.
ex:
import main
#patch('main.config.retry_times')
def test_method(self, mock_retry_times):
mock_retry_times().return_value = 'retry_times_mock_val'
#calling the main class method
in the main class method, retry_times is defined like this,
from source.config import config
def method():
var1 = config.retry_times['url']
# Do other stuff
This gave me an error as,
I tried with Magic Mock object as well. But it didn't work as per this solution. My imports also work fine.
But I figure out one thing.
when I check mock IDs in both the testable class and the test class they were different like this.
It seems like an issue in these ids. I think they must be the same in both classes. Does anyone help me to sortout this issue.

Related

Pytest -- class inheritance as a variable for setup

I have a setup class that has setup and teardown functions among other things. My test class inherits this setup class currently so that all tests are ran using those functions as well as access to some variables.
Here's what my setup class looks like:
class SetupClass1:
def setup(self):
...
def teardown(self):
...
Here's what my test class looks like:
class MyTestClass(SetupClass1):
def test_1(self):
...
def test_2(self):
...
I have another setup class, SetupClass2 with its own setup, teardown functions among other variables that are setup for tests to use. I was hoping to have it so that I could parameterize MyTestClass so take in both setup classes but haven't had much luck.
I was hoping to do something like this but it didn't work:
#pytest.mark.parameterize('setup_class', [SetupClass1, SetupClass2])
class MyTestClass(setup_class):
def test_1(self):
...
def test_2(self):
...
I'm not sure how to accomplish this or if I'm going about this the complete wrong way. There's a lot of tests that I'm working with so I was hoping to do minimal changes to accomplish this. I was reading into pytest.fixtures but I couldn't get them to work. Thanks in advance.

Python Pydantic double base model

I'm using FastAPI with Pydantic and I'm trying to achieve that my API accepts cammel case parameters, for this, I'm using the following
from pydantic import BaseModel
from humps import camelize
class CamelModel(BaseModel):
class Config:
alias_generator = camelize
allow_population_by_field_name = True
class MyClass(CamelModel):
my_field1: int
my_field2: int
my_field3: int
So far it works great, but MyClass is a base class for others classes, for example as
class MyNewClass(MyClass):
my_field4: float
How can I get the MyNewClass to also use the camel case base class? I've tried something like
from typing import Union
class MyNewClass(Union[MyClass, CamelModel]):
my_field4: float
But I'm getting this error
TypeError: Cannot subclass <class 'typing._SpecialForm'>
Is there any way to accomplish this?
Thanks!
What you are trying to achieve is called multiple inheritance. Since you are inheriting from a class which inherited from the CamelModel, there's no need to inherit it again
The appropriate code should be
class MyNewClass(MyClass):
It's the python syntax for multiple inheritance. See an extensive example here https://www.python-course.eu/python3_multiple_inheritance_example.php#An-Example-of-Multiple-Inheritance
The code you are using (class MyNewClass(Union[MyClass, CamelModel]):) is used for declaring data types, which is kinda of correct, but not in the right place. Typing is almost only (as far as I've seen) used for parameters of functions.
NOTE
I did not test the piece of code above, but I'm pretty sure it works. Let me know if there are any problems

Mocking in Odoo environment?

Does anyone know how can you write mock tests for Odoo objects?
I have these classes and methods:
my_module:
from odoo import models
class MyModel(models.Model):
_name = 'my.model'
def action_copy(self):
IrTranslation = self.env['ir.translation']
for rec in self:
if rec.translate:
IrTranslation.force_translation(rec)
my_module_2:
from odoo import models
class IrTranslation(models.Model):
_inherit = 'ir.translation'
def force_translation(self, rec):
# do stuff
When I call it, I want to test if IrTranslation.force_translation was called in action_copy method and how many times.
But this method is not imported directly, it is referenced through env.
If let say force_translation would be imported like:
from my_module_2.IrTranslation import force_translation
def action_copy(self):
# do stuff.
force_translation()
Then I could try doing something like this:
from unittest import mock
from my_module import action_copy
def test_some_1(self):
with mock.patch('my_module.my_module_2.IrTranslation') as mocked_translation:
action_copy()
mocked_translation.force_translation.assert_called_once()
But because modules in Odoo are not imported directly (like you do it in plain Python), I don't understand how to specify methods in Odoo environment to be mocked.
P.S. I also did not see any mocked tests in standard Odoo, except for base classes that do not inherit Model class -> which then you need to use its _inherit attribute instead of importing class and passing it to be inherited on another class.
Testing in Odoo does not use the concept of mocking. Instead, tests are derived from standard base classes. The standard class TransactionalTest opens a transaction and never commits it, but rolls it back to undo any changes.
This is obviously not the same as regular mocking in that you can't replace other methods or classes to return fixed/expected values and/or avoid other side effects apart from persisting changes in the database, like sending emails or calling a remote web service.
It can be done. I do it all the time since Odoo 8.0 (until 15.0 now). The key is to know where to patch. Odoo adds odoo.addons to your module's package when its imported so in your case, you may do the following:
from odoo import tests
from mock import patch
from odoo.addons.my_module_2.models.ir_translations import IrTranslation
class TestMyModule2(tests.TransactionCase):
def some_test_1(self):
my_model = self.env['my.model'].create({})
with patch.object(IrTranslation, 'force_translation') as mocked_translation:
my_model.action_copy()
mocked_translation.assert_called_once()
Or using just patch, then no need to import:
with patch('odoo.addons.my_module_2.models.ir_translations.IrTranslation.force_translation') as mocked_translation:
my_model.action_copy()
This patches your specific method in your specific class. This way you can also target the method of a super class.
If you need to patch a method and you don't care where it is or where it's overriden, just patch using Python's type() (then no need to import class):
with patch.object(type(self.env['ir.translation']), 'force_translation') as mocked_translation:
my_model.action_copy()
Some additional notes to save you some headaches:
If you use pyCharm, don't mock socket objects. It messes with
pyCharm's mechanismes. Better to put your calls to socket into a one line
method and mock that method instead.
datetime.datetime.now() cannot be mocked, as all builtin types, but fields.Datetime.now() can.

Getting all registered subclasses of an ABCMeta

I have a directory structure similar to the following:
.
├── main.py
├── model.py
└── models
├── __init__.py
├── model_a.py
└── model_b.py
model.py contains an Abstract Base Class:
from abc import ABCMeta, abstractmethod
class Base(metaclass=ABCMeta):
#abstractmethod
def run(self):
pass
in the modelsfolder are two implementations of this base class, model_a.py and model_b.py, who register themselves to the main Baseclass. model_a.py looks like this:
from model import Base
class ModelA(Base):
def run(self):
return "a"
ModelA.register(Base)
assert issubclass(ModelA, Base)
and model_b.pyis similar.
Now, what I am trying to do in main.py is to create a dictionary of all the subclasses of Base so that I can select one (via the GUI of my program) and run it:
from model import Base
subclasses = Base.__subclasses__()
dct = {cls.__name__: cls for cls in subclasses}
klass = dct['ModelA']
klass.run()
But I can't get it to work. I get RuntimeError: Refusing to create an inheritance cyclewhen I try to execute one of the derived classes and the dictionary in main.py is empty.
I realise this is rather late, but in case it's helpful to anyone else who stumbles upon this...
You've got a few problems here:
Your classes are the wrong way round in that register call; it would only make sense in this context to call Base.register(ModelA) (not the other way round) in order to register ModelA as a "virtual subclass" of Base.
Calling ModelA.register(Base) is trying to register Base as a virtual subclass of ModelA, but ModelA is already an actual subclass of Base, - which is why you're getting an inheritance cycle. You can't have classes X and Y inheriting from each other.
However, as ModelA is explicitly a subclass of Base, you don't need to call register at all. You want either:
class ModelA(Base):
...
with no register call (here ModelA is an actual subclass of Base), or:
class ModelA:
...
Base.register(ModelA)
(here ModelA is a standalone class, outside Base's inheritance hierarchy, but it is registered as a virtual subclass). Either/or - not both.
In either case, issubclass(ModelA, Base) would be True.
__subclasses__() doesn't pick up virtual subclasses, only actual ones - so if you want to use that, you should forget about register() and just make ModelA a real subclass of Base (the first option above).
(This is, to my mind, a wart with the whole ABC/register mechanism: issubclass() might be True but __subclasses__() doesn't pick it up - nasty.)
If you don't import the model containing ModelA at some point in your execution, it's never set up so ModelA won't show up in Base.__subclassess__() anyway. This is probably why the dictionary in main.py is empty.
A fix would be to add a line to main.py saying import models, and have models/__init__.py import model_a and model_b. Then when main runs, it imports models, which in turn imports model_a and model_a, executing the definitions of ModelA and ModelB and adding them to Base's class hierarchy.
On your final line, you don't instantiate an instance of whatever class klass is pointing at; the line should be:
klass().run()

patching boto3.resource and resource.Table method for unit testing

Here is my class:
# Production.py
import boto3
class Production(object):
resource = boto3.resource('dynamodb', 'us-west-2')
def __init__(self):
self.table = Production.resource.Table('employee')
I am trying to test that resource.Table is called with arg 'employee'. I wrote a unit test for it
def test_init():
with patch('Production.boto3.resource') as resource:
mock_resource = MagicMock()
resource.return_value = mock_resource
pd = Production()
resource.assert_called_with('dynamodb', 'us-west-2')
table = resource.return_value.Table.return_value
table.assert_called_with('employee')
test_init()
But it doesn't seem to work... Can some one help me how to test this?
When you patch an object it mocks all of its methods for you. So (I didn't test the code but) I think just:
def test_resource_is_called_with_correct_params():
with patch('Production.boto3') as mock_boto:
Production()
mock_boto.resource.assert_called_once_with('dynamodb', 'us-west-2')
will do the first part of your test. I would then test the init function separately in another test, which is clearer easier and more simple (generally aim to test one thing per test):
def test_table_is_called_with_correct_params():
with patch('Production.boto3') as mock_boto:
Production()
mock_resource = mock_boto.resource.return_value
mock_resource.Table.assert_called_once_with('employee')
I would say a couple of things about this though:
It's nice to group your tests into a class which organises your tests. Also when you get subclass TestsCase you get a bunch of methods that come with it such as self.assertDictEqual that will provide good meaningful output and work well will test runners like nose2. So do something like:
class TestProduction(unittest.TestCase):
def test1():
pass
def test2():
pass
The stuff you are testing there is basically hard coded so these tests are not really meaningful. You are just testing that the language works. I would learn to test behaviour rather than implementation ... So what do you want you class to do? Sit down and think about it first before you write the class. Then you can write the specs out and use those specs to design your tests before you even start coding.

Resources