From Intermediate-Usage Flask-RESTful 0.3.7 documentation
in the Passing Constructor Parameters Into Resources section at the bottom, how would you write a test in order to mock kwargs? Side note: I tweaked it so the Smart Engine class is passed directly rather than being instantiated to a variable then passed.
from flask_restful import Resource
class TodoNext(Resource):
def __init__(self, **kwargs):
# smart_engine is a black box dependency
self.smart_engine = kwargs['smart_engine']
def get(self):
return self.smart_engine.next_todo()
You can inject the required dependency into TodoNext like so:
api.add_resource(TodoNext, '/next',
resource_class_kwargs={ 'smart_engine': SmartEngine() })
Test class in question:
import unittest
class TestTodoNext(unittest.TestCase):
todo_next_instance = TodoNext() # How would you mock smart_engine in this case?
You can use Mock object from unittest.mock to mock smart_engine.
import unittest
from unittest.mock import Mock
class TestTodoNext(unittest.TestCase):
smart_engine = Mock()
smart_engine.next_todo.return_value = "YOUR DESIRED RETURN VALUE"
todo_next_instance = TodoNext(smart_engine=smart_engine)
self.assertEqual(todo_next_instace.get(), "YOUR DESIRED RETURN VALUE")
Related
Hello currently found a problem which I could not find solution to it.
I have a django application which communicates with external services in order to write unittests i must patch the functions which call the external services with mock.patch
#api_views.py
from examplemodule import examplefunction # just example
class GetSomeInfoFromExternalService(ApiView):
def get(self, *args, **kwargs):
if example_function(attrs) == somevalue:
return Response({'detail':'OK'})
return Response({'detail':'Not ok'})
here is my other file
#tests.py
from unittests import mock
from django.test import TestCase
class MyTestCase(TestCase):
#mock.pach('examplemodule.examplefunction')
def test_1(self):
examplefunction.return_value=123
Like that the patch method will not work, but if I import the examplefunction inside the ApiView.get method is overriding and the mock works.
#api_views.py
class GetSomeInfoFromExternalService(ApiView):
def get(self, *args, **kwargs):
from examplemodule import examplefunction # If the import is here the override is working properly
if example_function(attrs) == somevalue:
return Response({'detail':'OK'})
return Response({'detail':'Not ok'})
I am trying to patch a property of a class with some mock value, but when I run the test case the function or class that is using that property is not replacing it with the mock that I have created.
Module property that I need to mock
class Mymodule:
#property
async def calling_property(self):
return "Calling the property"
the class that is using this module.
class Usecase:
async def execute(self):
module = Mymodule()
data = await module.calling_property
print(data)
the test case I have written
import unittest
from unittest.mock import patch, AsyncMock,PropertyMock
class TestModule(unittest.IsolatedAsyncioTestCase):
async def test_execute(self):
with patch("Mymodule",
**{"calling_property":AsyncMock(return_value="calling mock")}, new_callable=PropertyMock
)as mock:
a = Usecase()
await a.execute()
As you can see i am logging the response from the property in my usecase, so when I run my test it is logging "calling the property" rather than "calling mock".
I tried another way.
class TestModule(unittest.IsolatedAsyncioTestCase):
async def test_execute(self):
with patch("Mymodule.calling_property",
new_callable=PropertyMock,
return_value= AsyncMock(return_value="calling mock")
)as mock:
a = A()
await a.execute()
This gives separate error
TypeError: object AsyncMock can't be used in 'await' expression
I have also tried creating a mock for the class object
class TestModule(unittest.IsolatedAsyncioTestCase):
async def test_execute(self):
with patch.object(Mymodule,
"calling_property",
new_callable=PropertyMock,
return_value=AsyncMock(return_value="calling mock")
)as mock:
a = A()
await a.execute()
TypeError: object AsyncMock can't be used in 'await' expression
I am trying to mock secret manager. Here is the code which is written for secret manager using boto3 which I am trying to mock and test.
utils.py
import boto3
secret_id = os.environ.get("SECRETS")
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId=secret_id)
secrets = json.loads(response['SecretString'])
S3_BUCKET_NAME = secrets["S3_BUCKET_NAME"]
SQS_QUEUE_NAME = secrets["SQS_Queue_Name"]
these variables are then used in different methods.
conftest.py
#pytest.fixture(scope='session', autouse=True)
def secret_manager_resource(aws_credentials):
"""Secret Manager mock client"""
with mock_secretsmanager():
conn = boto3.client("secretsmanager", region_name="us-east-1")
logger.info(f"Secret manager connection {conn}")
yield conn
test_file.py
#contextmanager
def secret_manager_setup(secret_manager_resource):
secret_manager_resource.create_secret(Name="test", SecretString="""{"S3_BUCKET_NAME": "test","SQS_Queue_Name": "test_queue"}""")
yield
class TestSecretManager:
def test_secret_manager(self, secret_manager_resource):
with secret_manager_setup(secret_manager_resource):
try:
result = secret_manager_resource.get_secret_value(SecretId="test")
json_result = json.loads(result['SecretString'])
assert json_result["S3_BUCKET_NAME"] == "test"
assert json_result["SQS_Queue_Name"] == "test_queue"
except Exception as err:
print("Error ---", err)
class TestClass:
def test_some_class(test_var):
from functions.something.some import something
something = someClass({}, param)
When I run pytest it directly goes inside TestClass and calls secret Manager and throws error as it is trying to connect to actual secret manager. Could someone suggest me what can be done to over come this issue?
TestClass is not mocked - so I wouldn't expect that to work. You could use Moto as a class-decorator to ensure everything inside someClass is mocked.
Note that the class-decorator creates a mock around test-methods only, so the code-under-test would have to be inside a test-method for this to work.
#mock_secretsmanager()
class TestClass:
def test_something():
from functions.something.some import something
something = someClass({}, param)
See http://docs.getmoto.org/en/latest/docs/getting_started.html#class-decorator for the documentation and more examples around this.
I am trying to do the following:
#patch('uuid.uuid4', autospec=True)
def test_generate_adid(self, patched_uuid, app_api):
patched_uuid.return_value = "9e51ab81-6d65-4b81-af3b-8f7f49d69ba7"
adid = app_api.generate_adid()
assert adid == "9e51ab81-6d65-4b81-af3b-8f7f49d69ba7"
Where app_api is a fixture of the class under test.
However, in my app_api class, uuid4() is not getting patched and keeps returning a uuid other than the one I am trying to force. Here is what the generate_adid() instance method looks like:
from uuid import uuid4
def generate_adid(self):
adid = str(uuid4())
return adid
The failing unit test error:
AssertionError: assert '90b29e86-e3b0-40aa-8971-f868f90cb009' == '9e51ab81-6d65-4b81-af3b-8f7f49d69ba7'
I have consulted this post: How to mock uuid generation in a test case? but still am having no luck.
What am I doing wrong? Thanks to all of those who reply in advance.
EDIT: Here is the full code:
from requests import Session
from random import uniform
from hashlib import md5
from hmac import new
from uuid import uuid4
from json import dumps
class AppApi:
def __init__(self, account):
self.account = account
self.session = Session()
def generate_adid(self):
adid = str(uuid4())
return adid
Test Case:
from src import AppApi
from pytest import fixture
from unittest.mock import patch
from json import loads
ACCOUNT = {
"email": "user#email.com",
"username": "user",
"password": "s3cr3t"
}
#fixture
def app_api():
app_api = AppApi(ACCOUNT)
yield app_api
class TestAppApi:
#patch('uuid.uuid4')
def test_generate_adid(self, patched_uuid, app_api):
patched_uuid.return_value = "9e51ab81-6d65-4b81-af3b-8f7f49d69ba7"
adid = app_api.generate_adid()
assert adid == "9e51ab81-6d65-4b81-af3b-8f7f49d69ba7"
In your example you're patching the uuid4() function in the uuid module rather than the function uuid4() in the module which you're trying to test. Take a look at Python unnit.test docs where to patch
Using your example above you need to patch the uuid4() imported into the src module. You need to use #patch("src.uuid4")
from src import AppApi
from pytest import fixture
from unittest.mock import patch
from json import loads
ACCOUNT = {
"email": "user#email.com",
"username": "user",
"password": "s3cr3t"
}
#fixture
def app_api():
app_api = AppApi(ACCOUNT)
yield app_api
class TestAppApi:
#patch('src.uuid4')
def test_generate_adid(self, patched_uuid, app_api):
patched_uuid.return_value = "9e51ab81-6d65-4b81-af3b-8f7f49d69ba7"
adid = app_api.generate_adid()
assert adid == "9e51ab81-6d65-4b81-af3b-8f7f49d69ba7"
Hope this helps!
I have following file and i am running pytests on this file. One of the use case is to test the logger function which is global. How can we mock this in my pytest ?
import spark_fn
def run_job(params):
do_something()
logger.info("some info")
def main(params):
global logger
app_name = "test app"
spark, logger = spark_fn(app_name)
run_job(params)
if __name__ == "__main__":
params = "some params"
main(params)
===============================================================
How's something like this? You should think of it less like a global variable and more like patching what spark_fn is returning to have a "fake" logger class. Also I'm curious how you're using the spark_fn module overall with how you're importing it.
import yoursampletest
def test_monkeytest(monkeypatch):
"""
Monkey patches a fake logger into spark_fn
"""
class myFakeLogger:
def info(self, value):
return value
def mock_info(mylogstatement):
return 1, myFakeLogger()
monkeypatch.setattr(yoursampletest, 'spark_fn', mock_info)
assert yoursampletest.main(1) == 'some info'