Pytest - How to mock or patch global variables - python-3.x

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'

Related

mock secret manager using pytest

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.

How can I run pytest in another folders in Python

In folder [Root]/src/app, I have a file services_factory.py, for example:
class Describing:
def __init__(self):
pass
def get_description(self):
pass
class APIService(Describing):
def __init__(self):
pass
def get_description(self):
return 'Here provide services for APIs'
class DatabaseService(Describing):
def __init__(self):
pass
def get_description(self):
return 'Here provide services for Database'
class Injector:
def __init__(self):
pass
def get_service(self, type='API'):
services = {
"API": APIService,
"DB": DatabaseService
}
return services[type]()
At the end of file services_factory.py, I add an unittest, ex:
def test_services_injector():
injector = Injector()
api_service = injector.get_service('API')
db_service = injector.get_service('DB')
assert api_service.get_description() == 'Here provide services for APIs'
assert db_service.get_description() == 'Here provide services for Database'
Then, cmd: $ pytest src/app/services_injector.py, it worked nicely.
But when I create a file test_services_factory.py in [Root]/tests/app, for example:
import unittest
from unittest.mock import patch
def test_services_injector():
assert 'a' == 'a'
I can't import the classes in my services_factory.py.
So, how can I quickly fix this problem?

How to Mock a flask-restful class with kwargs

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")

Unit Test mocking AWS Lambda Function with global section, Python

I'm writing a python lambda with an initialisation to an external service in the global section of the lambda. How do I mock this external dependency as it is initialised at the creation of my class?
data_load.py
from elasticsearch import Elasticsearch,
# Global section - executed once for the life time of the container
def get_elastic_search_connection():
try:
client = Elasticsearch(
hosts=[{'host': "host", 'port': 9191}],
use_ssl="true",
verify_certs="true",
connection_class=RequestsHttpConnection)
return client
except Exception as e:
LOGGER.error("Unable to connect to ES")
exit(1)
es_client = get_elastic_search_connection()
# handler section - executed once per function invocation
def lambda_handler(event, context):
es_client.bulk(body="body", index="index", doc_type="doc", _source=False)
In my test, as soon as the import statement imports data_load.py, it tries to initialise the global section of the lambda, which tries to make a http connection to host address. How do I create a partial mock of data_loay.py so that I can test the lambda_handler method? The es_client has to be initialised in the global section. How do I dependency inject a mock into the global section?
test_data_load.py
import unittest
import search_lambda.data_load.data_load
class TestDatLoad(unittest.TestCase):
def test_data_load(self):
print("Hello")
if __name__ == '__main__':
unittest.main()

How can I do http.server do_GET mock unit test?

I am beginner in Unit test. I would like to know how can I do mock unit test of following functions.
import ...
class A(BaseHTTPRequestHandler):
def do_GET(self):
client_ip = self.client_address[0]
if client_id == '10.10.10.10':
self._set_headers_200()
return
else:
self._set_headers_400()
return
Test Class:
import unittest
def test_A_get():
I want to test both 200 and 400 response
Can anybody help me this problem?
I'm working on a similar task. Here is a partial solution I've came up with so far
class TestA(unittest.TestCase):
def setUp(self):
self.mock_request = Mock()
#patch('http.server.HTTPServer')
#patch('my_module.A.do_GET')
def test_do_get(self, mock_do_get, mock_http_server):
"""Test if do_GET method gets called"""
mock_do_get.return_value = "/"
self.mock_request.makefile.return_value = IO(b"GET /")
server = A(self.mock_request, ('127.0.0.1', 8080), mock_http_server)
self.assertTrue(mock_do_get.called)
self.assertEqual(server.do_GET(), "/")

Resources