Python3 mock on setup - python

I am trying to mock some 3rd part library on setup so i can pretend it works as expected on my code.
I was able to mock it locally on which i configure all the returns on the function itself
class MockConnecton:
def __init__(self):
self._ch = Mock()
def channel(self):
return self._ch
class QEmiterTest(unittest.TestCase):
#patch('task_queues.queue.pika.BlockingConnection')
#patch('task_queues.queue.pika.ConnectionParameters')
def test_emiter(self,mock_params,mock_block):
config = {
'host':'mq',
'exchange':'test'
}
params = {"FOO":"BAR"}
mock_params.return_value = params
conn = MockConnecton()
mock_conn = Mock(wraps=conn)
mock_block.return_value = mock_conn
emitter = QEmitter(config['host'],config['exchange'])
mock_params.assert_called_with(config['host'])
mock_block.assert_called_with(params)
mock_conn.channel.assert_called_with()
conn._ch.exchange_declare.assert_called_with(exchange=config['exchange'],type='topic')
But when i try to move from this approach to a cleaner one with the mock start/stop i receive an error on the assertion:
AttributeError: '_patch' object has no attribute 'assert_called_with'
I am trying to port it like this
class QEmiterTest(unittest.TestCase):
def setUp(self):
mock_params = patch('task_queues.queue.pika.ConnectionParameters')
mock_block = patch('task_queues.queue.pika.BlockingConnection')
self.params_ret = {"FOO":"BAR"}
mock_params.return_value = self.params_ret
conn = MockConnecton()
self.mock_conn = Mock(wraps=conn)
mock_block.return_value = self.mock_conn
self.patch_params = mock_params
self.patch_block = mock_block
self.patch_params.start()
self.patch_block.start()
def test_emiter(self):
config = {
'host':'mq',
'exchange':'test'
}
emitter = QEmitter(config['host'],config['exchange'])
self.patch_params.assert_called_with(config['host'])
self.patch_block.assert_called_with(self.params_ret)
self.mock_conn.channel.assert_called_with()
self.mock_conn._ch.exchange_declare.assert_called_with(exchange=config['exchange'],type='topic')
def tearDown(self):
self.patch_params.stop()
self.patch_block.stop()
I may not fully understand the start and stop, i was under the assumpton that on setup it would apply the patch and by it's reference i would be able to make assertions . I also welcome any suggestons on how to make several mocks cleaner

patch object are patch and not mock. mock framework have two main duties:
Mock object used to record the call and follow your screenplay
Patch methods and object used to replace reference by something that you can use to sensor or simulate some behaviors
A lot of time we can use patch to install mocks... but patch are not mock.
patch.start() return the new reference (often a mock) used to patch the original one.
def setUp(self):
self.params_ret = {"FOO":"BAR"}
self.mock_conn = Mock(wraps=conn)
self.patch_params = patch('task_queues.queue.pika.ConnectionParameters', return_value = self.params_ret)
self.patch_block = patch('task_queues.queue.pika.BlockingConnection', return_value=self.mock_conn)
mock_params = self.patch_params.start()
mock_block = self.patch_block.start()

Related

Django model error with nested dictionaries

I am relatively new to Django, but not to python, My model is trying to use a class (defined in a separate file) in which data is coming from a REST API, the retrieved data is in a nested dictionary. The code will run fine in python, but when I try it in Django (makemigrations), I get an error:
File "c:\blah-blah\Clone_PR.py", line 20, in GetFoundOnSelectItems
values = self._issueEdit["fields"]["customfield_13940"]["allowedValues"]
TypeError: 'NoneType' object is not subscriptable
I tried using type hints, but that does not work either.
models.py
from dal import autocomplete
from django.db import models
from django.contrib import messages
from .Login import jlogin
from .Jira_Constants import ProductionServer, TestServer, StageServer
from .Clone_PR import Issue
jira = None
issue = Issue()
class ClonePrLogin(models.Model):
username = models.CharField(max_length=30)
password = models.CharField(max_length=30)
#classmethod
def LoginToJira(cls):
global jira
jira = jlogin(ProductionServer, cls.username, cls.password)
class PrEntry(models.Model):
prToClone = models.CharField(max_length=20)
#classmethod
def GetIssueAndMeta(cls):
global issue
issue.initialize(jira, cls.prToClone)
class ClonePr(models.Model):
issueKey = issue.issueKey
issue.GetFoundOnSelectItems()
foundOnList = issue.foundOnSelectItems
foundOn = autocomplete.Select2ListChoiceField(choice_list=foundOnList)
Clone_PR.py
from typing import List, Dict
class Issue():
def __init__(self):
self.jiraInst = None
self.issueKey = ''
self._issue = None
self._issueEdit = None
# self._issueEdit = Dict[str, Dict[str, Dict[str, List[Dict[str, str]]]]]
self.foundOnSelectItems = []
def initialize(self, jira, prKey):
self.jiraInst = jira
self.issueKey = prKey
self._issue = jira.issue(prKey)
self._issueEdit = jira.editmeta(prKey)
def GetFoundOnSelectItems(self):
values = self._issueEdit["fields"]["customfield_13940"]["allowedValues"]
items = [x["value"] for x in values]
self.foundOnSelectItems = items
In Django, running makemigrations will load all the modules. You said you're familiar with Python so you should know that the declarations inside the class:
class ClonePr(models.Model):
issueKey = issue.issueKey
issue.GetFoundOnSelectItems()
foundOnList = issue.foundOnSelectItems
foundOn = autocomplete.Select2ListChoiceField(choice_list=foundOnList)
will run when the modules load. You're calling issue.GetFoundOnSelectItems() at that time, which in turn calls values = self._issueEdit["fields"]["customfield_13940"]["allowedValues"], except that self._issueEdit = None upon the initiation of instance Issue above with this line: issue = Issue().
I highly recommend you spend some time to become more familiar with how Django starts up an app. The module-level and nested model declarations here are both antipatterns and may cause data issues in the future.

Can't get list of model fields

I need to get list of model fields like:
#instance.register
class Todo(Document):
title = fields.StringField(required=True, default='Name')
description = fields.StringField()
created_at = fields.DateTimeField()
created_by = fields.StringField()
priority = fields.IntegerField()
to
[
'title',
'description',
'created_at',
'created_by',
'priority'
]
So, I have function that returns list of fields
def get_class_properties(cls):
attributes = inspect.getmembers(cls, lambda a: not (inspect.isroutine(a)))
return [attr for attr in attributes if not (attr[0].startswith('__') and attr[0].endswith('__'))][1]
But usage gives me this error
umongo.exceptions.NoDBDefinedError: init must be called to define a db
Usage:
properties=get_class_properties(Todo)
UPD
Here is my mongo initialization code:
async def mongo_client(app):
conf = app["config"]["mongo"]
client = AsyncIOMotorClient(host=conf["host"], port=conf["port"])
db = client[conf["db"]]
instance.init(db)
await Todo.ensure_indexes()
app["db_client"]: AsyncIOMotorClient = client
app["db"] = db
yield
await app["db_client"].close()
This is a copy/paste of this answer from the author of this library:
As far as I remeber, this exception raises when you're trying to use
lazy clients without initializing them properly. Any lazy class of
uMongo expects that the used database will be specified before the
usage. Everything that you need is to specify the used database and
invoke the init method of your lazy instance, like this:
from motor.motor_asyncio import AsyncIOMotorClient
from umongo import MotorAsyncIOInstance
client = AsyncIOMotorClient("mongodb://user:password#host:port/")
client = client["test_database"]
lazy_umongo = MotorAsyncIOInstance()
lazy_umongo.init(client)
As an example you can look into Auth/Auth microservice code, where
documents defined and store in the separate files from the actual
usage. Also these files with code as examples (documents.py and
prepare_mongodb.py) will help you to find a solution.
The trick was that
properties=get_class_properties(Todo)
invokes earlier than
async def mongo_client(app):
Solution is use things in right order (see comments to code)
async def init_app(argv=None):
app = web.Application(middlewares=[deserializer_middleware], logger=logger)
app["config"] = config
conf = app["config"]["mongo"]
client = AsyncIOMotorClient(host=conf["host"], port=conf["port"])
db = client[conf["db"]]
instance.init(db)
# Remove this line:
# app.cleanup_ctx.append(mongo_client)
app.cleanup_ctx.append(api_client)
register_routes(app)
return app
def register_routes(app: web.Application):
# Use here:
todo_resource = RestResource(
entity='todo',
factory=Todo,
properties=get_class_properties(Todo)
)
todo_resource.register(app.router)

Mocking return value of a nested call in Python mock library

Brand new to this library
Here is the call stack of my mocked object
[call(),
call('test'),
call().instance('test'),
call().instance().database('test'),
call().instance().database().snapshot(),
call().instance().database().snapshot().__enter__(),
call().instance().database().snapshot().__enter__().execute_sql('SELECT * FROM users'),
call().instance().database().snapshot().__exit__(None, None, None),
call().instance().database().snapshot().__enter__().execute_sql().__iter__()]
Here is the code I have used
#mock.patch('testmodule.Client')
def test_read_with_query(self, mock_client):
mock = mock_client()
pipeline = TestPipeline()
records = pipeline | ReadFromSpanner(TEST_PROJECT_ID, TEST_INSTANCE_ID, self.database_id).with_query('SELECT * FROM users')
pipeline.run()
print mock_client.mock_calls
exit()
I want to mock this whole stack that eventually it gives me some fake data which I will provide as a return value.
The code being tested is
spanner_client = Client(self.project_id)
instance = spanner_client.instance(self.instance_id)
database = instance.database(self.database_id)
with database.snapshot() as snapshot:
results = snapshot.execute_sql(self.query)
So my requirements is that the results variable should contain the data I will provide.
How can I provide a return value to such a nested calls
Thanks
Create separate MagicMock instances for the instance, database and snapshot objects in the code under test. Use return_value to configure the return values of each method. Here is an example. I simplified the method under test to just be a free standing function called mut.
# test_module.py : the module under test
class Client:
pass
def mut(project_id, instance_id, database_id, query):
spanner_client = Client(project_id)
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)
with database.snapshot() as snapshot:
results = snapshot.execute_sql(query)
return results
# test code (pytest)
from unittest.mock import MagicMock
from unittest import mock
from test_module import mut
#mock.patch('test_module.Client')
def test_read_with_query(mock_client_class):
mock_client = MagicMock()
mock_instance = MagicMock()
mock_database = MagicMock()
mock_snapshot = MagicMock()
expected = 'fake query results'
mock_client_class.return_value = mock_client
mock_client.instance.return_value = mock_instance
mock_instance.database.return_value = mock_database
mock_database.snapshot.return_value = mock_snapshot
mock_snapshot.execute_sql.return_value = expected
mock_snapshot.__enter__.return_value = mock_snapshot
observed = mut(29, 42, 77, 'select *')
mock_client_class.assert_called_once_with(29)
mock_client.instance.assert_called_once_with(42)
mock_instance.database.assert_called_once_with(77)
mock_database.snapshot.assert_called_once_with()
mock_snapshot.__enter__.assert_called_once_with()
mock_snapshot.execute_sql.assert_called_once_with('select *')
assert observed == expected
This test is kind of portly. Consider breaking it apart by using a fixture and a before function that sets up the mocks.
Either set the value directly to your Mock instance (those enters and exit should have not seen) with:
mock.return_value.instance.return_value.database.return_value.snapshot.return_value.execute_sql.return_value = MY_MOCKED_DATA
or patch and set return_value to target method, something like:
#mock.patch('database_engine.execute_sql', return_value=MY_MOCKED_DATA)

is there a faster way to write similar test cases for Django views?

Basically, I realize that I am writing the same test case (test_update_with_only_1_field) for a similar URL for multiple models
from django.test import RequestFactory, TestCase
class BaseApiTest(TestCase):
def setUp(self):
superuser = User.objects.create_superuser('test', 'test#api.com', 'testpassword')
self.factory = RequestFactory()
self.user = superuser
self.client.login(username=superuser.username, password='testpassword')
class SomeModelApiTests(base_tests.BaseApiTest):
def test_update_with_only_1_field(self):
"""
Tests for update only 1 field
GIVEN the following shape and related are valid
WHEN we update only with just 1 field
THEN we expect the update to be successful
"""
shape_data = {
'name': 'test shape',
'name_en': 'test shape en',
'name_zh_hans': 'test shape zh hans',
'serial_number': 'test shape serial number',
'model_name': {
'some_field': '123'
}
}
data = json.dumps(shape_data)
response = self.client.post(reverse('shape-list-create'), data, 'application/json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
some_model = response.data['some_model']
new_some_field = '12345'
data = json.dumps({'some_field': new_some_field, 'id': response.data['some_model']['id']})
response = self.client.put(reverse('some-model', args=[some_model['id']]), data, 'application/json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(new_some_field, response.data['some_field'])
I need to do this for more than 10 times. Which I have already done so.
the only difference each time, is the following phrases "some_model", "some-model", and "some_field"
I was wondering if there's a faster way to do this.
I can think abstractly two ways:
create a template in a text editor that somehow can generate the final test case which I then copy and paste. I am using sublime text 3 though I am okay to switch to another text editor
There's a way I can write slightly more code in the form of converting this test case into a behavior class that the individual test class can call. aka composition.
Which one makes more sense or there's a different way to do this?
Please note that BaseApi class is also inherited by other test class that do NOT have that repetitive test case method.
I guess what you want is "parameterized tests", standard unittest could do this with parameterized package:
import unittest
from parameterized import parameterized
class SomeModelApiTests(unittest.TestCase):
#parameterized.expand([
('case1', 'm1', 'f1', 'nf1'),
('case1', 'm2', 'f2', 'nf2'),
])
def test_update_with_only_1_field(self, dummy_subtest_name, model_name, field_name, new_field_value):
print(model_name, field_name, new_field_value)
will yields:
test_update_with_only_1_field_0_case1 (t.SomeModelApiTests) ... m1 f1 nf1
ok
test_update_with_only_1_field_1_case1 (t.SomeModelApiTests) ... m2 f2 nf2
ok
pytest testing framework has better support builtin on parameterized tests, worth looking at.
You could create a list / dict of "some_model" to test, and use subtest (https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests) for each of your "some_model" items.
my_list_of_model = [FirstModel, SecondModel]
for my_model in my_list_of_model:
with subTest(model=mymodel):
# Testing model here
If you want a different TestCase for each of your model, I think the multiple inheritance is the way to go:
class BaseApiTestCase(TestCase):
def setUp():
# Setup stuff
class RepetitiveTestCaseMixin:
# Class to do the repetitive stuff
def test_update_should_work(self):
# Do some thing with self.model and self.field here
class ModelTestCase(BaseApiTestCase, RepetitiveTestCaseMixin):
#classmethod
def setUpClass(cls):
super().setUpClass()
cls.model = MyModel
cls.field = 'some_field'
Projects I work on we sometimes use mixin + "customization hooks" when a test needs to repeated. (and endpoints like the "shape-list-create" is subject to change/refactored)
Example for question:
class TestUpdateWithOnly1FieldMixin(object):
some_model = None
some_field = None
some_model2 = None
def get_some_model(self):
return self.some_model
def get_some_field(self):
return self.some_field
def get_some_model2(self):
return self.some_model2
def test_update_with_only_1_field(self):
some_model = self.get_some_model()
# represents some-model in example
some_model2 = self.get_some_model2()
some_field = self.get_some_field()
shape_data = {
'name': 'test shape',
'name_en': 'test shape en',
'name_zh_hans': 'test shape zh hans',
'serial_number': 'test shape serial number',
'model_name': {
some_field: '123'
}
}
data = json.dumps(shape_data)
response = self.client.post(reverse('shape-list-create'), data, 'application/json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
some_model_data = response.data[some_model]
class SomeModelApiTests(base_tests.BaseApiTest, TestUpdateWithOnly1FieldMixin):
some_model = 'choose your model'
some_field = 'some_field'
some_model2 = 'some-model'
def get_some_field(self):
# Do customization
return 'some-field after customize'
How to split the customization hooks and what to put in mixin etc is based on the situation.
In my opinion the goal is to have the actual test case easy to follow. (Maybe move the "post shape-list-create" into a separate function as it might not really be relevant for that test case)
Another example, going a bit overboard with customizations but just to give an idea.
class TestWithGoodNameMixin(object):
some_model = None
some_field = None
# "Customization hooks"
def get_shape_data(self):
return {self.some_field: 'x'}
def create_model(self, shape_data):
response = self.client.post(reverse('shape-list-create'), shape_data,
'application/json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
return response[self.some_model]
def create_put_data(self, some_model_data):
# Add default implementation
pass
# .....
def test_update_with_only_1_field(self):
shape_data = self.get_shape_data()
some_model_data = self.create_model(shape_data)
data = self.create_put_data(some_model_data)
response = self.put_data(data)
self.assert_put_response(response)
You can use pytest package for unit testing.
It is very simple and easy to use.
#pytest.mark.parametrize() decorator can be used to achieve that functionality.
An example for parametrized test cases is as follows:
import pytest
class SampleTesting(object):
data_for_test = [
('{inputdata1:value1}','output1'),
('{inputdata1:value2}','output2')
]
#pytest.mark.parametrized('input_data, expected_output', data_for_test)
def test_sample_function(self, input_data, expected_output):
response = function_to_be_tested(input_data)
assert response == expected_output
You can read more about this decorator in the docs'
You can also use the #pytest.fixture() decorator to setup the test function.

Python SafeConfigParser mocking

I'm studying how to use mocking in my unit test program.
Now I have a SafeConfigParser object and I want to test what I write is correct.
After google the mocking usage of SafeConfigParser, I already know how to test the read of SafeConfigParser. But I still don't know how to verify the write of SafeConfigParser.
My idea is:
Make a empty buffer.
Consider a method that can set the buffer to SafeConfigParser.
Call the function which include SafeConfigParser.write()
Verify the buffer with my answer.
My program which need to be tested is like following:
def write_tokens_to_config(self):
"""Write token values to the config
"""
parser = SafeConfigParser()
with open(self.CONFIG_PATH) as fp:
parser.readfp(fp)
if not parser.has_section('Token'):
parser.add_section('Token')
parser.set('Token', 'access_token', self._access_token)
parser.set('Token', 'refresh_token', self._refresh_token)
with open(self.CONFIG_PATH, 'wb') as fp:
parser.write(fp)
P.S. You can check the read part from this url: http://www.snip2code.com/Snippet/4347/
I finally find out a solution :).
I modify my program(ex: program.py) to the followings:
class Program():
def __init__(self):
self._access_token = None
self._refresh_token = None
self.CONFIG_PATH = 'test.conf'
def write_tokens_to_config(self):
"""Write token value to the config
"""
parser = SafeConfigParser()
parser.read(self.CONFIG_PATH)
if not parser.has_section('Token'):
parser.add_section('Token')
parser.set('Token', 'access_token', self._access_token)
parser.set('Token', 'refresh_token', self._refresh_token)
with open(self.CONFIG_PATH, 'wb') as f:
parser.write(f)
And my test program like this:
class TestMyProgram(unittest.TestCase):
def setUp(self):
from program import Program
self.program = Program()
def test_write_tokens_to_config(self):
from mock import mock_open
from mock import call
self.program._access_token = 'aaa'
self.program._refresh_token = 'bbb'
with mock.patch('program.ConfigParser.SafeConfigParser.read'):
m = mock_open()
with mock.patch('__builtin__.open', m, create=True):
self.program.write_tokens_to_config()
m.assert_called_once_with(self.program.CONFIG_PATH, 'wb')
handle = m()
handle.write.assert_has_calls(
[
call('[Token]\n'),
call('access_token = aaa\n'),
call('refresh_token = bbb\n'),
]
)
Ref: http://docs.python.org/dev/library/unittest.mock#mock-open

Categories