pytest mocker two separate external calls for the one process - python-3.x

I have a process that executes a method, let's say called 'save_details', which runs a loop to go out to an external source twice. I want to be able to mock the two responses that would be returned to 'save_details', to give me two different IDs. I can do it when I have one response, but it doesn't seem to work when I need two mock responses to be returned.
So I have the following example fixtures.
Mock 1
#pytest.fixture
def test_201_mock1(mocker) -> None:
"""
Test return data for the 201 response.
"""
mocker.patch(
"save_details",
return_value=[
"201",
{
"ACK": {
"id": "e944126b-db78-4711-9f97-83c2cd1e09a4",
}
},
],
)
Mock 2
#pytest.fixture
def test_201_mock2(mocker) -> None:
"""
Test return data for the 201 response.
"""
mocker.patch(
"save_details",
return_value=[
"201",
{
"ACK": {
"id": "4758a428-8f33-4dc8-bb64-a500855f9a8c",
}
},
],
)
And I have my test:
async def test_create_valid_resource_201(
test_201_mock1,
test_201_mock2,
...
) -> None:
"""
Tests if a valid POST request returns a 201 response.
"""
#rest of test
...
The 'test_create_valid_resource_201' test will simply run the 'save_details' method I have. What happens is I basically get the ID from test_201_mock2 duplicated.
4758a428-8f33-4dc8-bb64-a500855f9a8c
4758a428-8f33-4dc8-bb64-a500855f9a8c
How would I get pytest to recognise that there are two mocks to be returned, one for each iteration of my loop in 'save_details'? Do I create one mock with the two responses for example?

Side effect is meant just for this purpose - it can be used to return different values in the same mock. This example is taken from the documentation - 3 consecutive calls to the mock would produce different results as specified in side_effect:
>>> mock = MagicMock()
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)
In your example you can have a single fixture to set the side_effect like so:
#pytest.fixture
def test_201_mock(mocker) -> None:
"""
Test return data for the 201 response.
"""
mocker.patch(
"save_details",
side_effect=[
["201",
{
"ACK": {
"id": "e944126b-db78-4711-9f97-83c2cd1e09a4",
}
}],
["201",
{
"ACK": {
"id": "4758a428-8f33-4dc8-bb64-a500855f9a8c",
}
}]
],
)

Related

How to manually clear DB in a Django test?

I am using:
python = "3.8.3",
django="3.0.5"
I have written a django test with APITestCase. I am running other tests inside my test class. I am trying to call other functions inside my test class. In order to do this I have a dictionary in a list which i mapped like this:
[
{
"class_name": class_name,
"func_name": "test_add_product", # Comes test name
"trigger_type": trigger_type, # Comes from model choices field.
"request_type": RequestTypes.POST,
"success": True,
},
{
...
},
]
I am looping these with a for loop and running each one. After each loop db should be cleared in order to not get error. I tried to do this using:
# Lets say that, we have a Foo model
Foo.objects.all().delete()
This method works, but I want a better solution.
How can I manually clear the test db before the test finishes?
you can do this
from django.test import Client, TestCase
class TestsDB(TestCase):
def __init__(self):
self.e = Client()
def test_delete_db(self):
foo = Foo.objects.all()
foo.delete()

Automatic method delegation in Python

I have a rather contrived code here :
backend_data = {
"admins": ["Leo", "Martin", "Thomas", "Katrin"],
"members": [
"Leo",
"Martin",
"Thomas",
"Katrin",
"Subhayan",
"Clemens",
"Thoralf"
],
"juniors": ["Orianne", "Antonia", "Sarah"]
}
class Backend:
def __init__(self, data):
self.backend_data = data
def get_all_admins(self):
return self.backend_data.get("admins")
def get_all_members(self):
return self.backend_data.get("members")
def get_all_juniors(self):
return self.backend_data.get("juniors")
class BackendAdaptor:
# Does some conversion and validation
def __init__(self, backend):
self.backend = backend
def get_all_admins(self):
return (admin for admin in self.backend.get_all_admins())
def get_all_members(self):
return (member for member in self.backend.get_all_members() if member not in self.backend.get_all_admins())
def get_all_juniors(self):
return (junior for junior in self.backend.get_all_juniors())
if __name__ == "__main__":
backend = Backend(data=backend_data)
adaptor = BackendAdaptor(backend=backend)
print(f"All admins are : {list(adaptor.get_all_admins())}")
print(f"All members are : {list(adaptor.get_all_members())}")
print(f"All juniors are : {list(adaptor.get_all_juniors())}")
So the BackendAdaptor class basically would be used to do some validation and conversion of the data that we get from the Backend .
The client should only be asked to interact with the API of the BackendAdaptor which is exactly similar to that of Backend . The adaptor class sits in middle and gets data from Backend does some validation if required and the gives back the data to client.
The issue is that the validation on the data that is getting returned from the Backend is not done for every method(For ex: there is validation done on get_all_members but not on get_all_admins and also not on get_all_juniors). The method just gives back a generator on whatever data it gets from Backend.
As is the case now i still have to implement a one liner methods for them .
Is there a way in Python to avoid this ? I am thinking in lines of magic methods like __getattribute__ ? But i have no idea on how to do this for methods.
So the best case scenario is this:
I implement the methods for which i know that i have to do some validation on Backend data
For the rest of the methods it is automatically delegated to Backend and then i just return a generator from what i get back
Any help would be greatly appreciated.
You can implement __getattr__. It is only called if a non-existing attribute is accessed. This will return some generic function with the desired functionality.
class BackendAdaptor:
def __init__(self, backend):
self.backend = backend
def __getattr__(self, name):
if not hasattr(self.backend, name):
raise AttributeError(f"'{name}' not in backend.")
return lambda: (i for i in getattr(self.backend, name)())
def get_all_members(self):
return (member for member in self.backend.get_all_members() if member not in self.backend.get_all_admins())

groovy to get the json key values to a string

I have a json file with values - {"testID":"smoke_01","testID":"smoke_02","testID":"smoke_04"}.
I read the values for testID and saved to array. Now I was trying to make it as a simple string like -
testcase = smoke_01,smoke_02,smoke_02. My code is below :
def props = readJSON file: 'input.json'
def list = []
props.each { key, value ->
list.push("$value")
}
echo "$list"
def asString = list.join(", ")
def testcase = asString.join(", ")
But when i print testcase - its printing as [smoke_01, smoke_02, smoke_04] . Please help me to understand where I am wrong.
Assuming you really have this invalid strange JSON (keys must be unique in an Object in basically every language you parse to, but yours are not), and that readJSON actually parses your custom JSON into something you can iterate over as if it were a Map... then, this should do what you want:
def props = readJSON file: 'input.json'
def testCase = props.values().join(', ')
println testCase
Assuming you use proper JSON
By proper JSON I mean something that does not break JSON semantics, hence can be parsed by any JSON parser, not your custom one.
You JSON file should look more like this:
{ "tests": [
{ "testID": "smoke_01" },
{ "testID": "smoke_02" },
{ "testID": "smoke_03" }
] }
In which case you would parse it with:
def json = new JsonSlurper().parse(new File('input.json'))
def testCase = json.tests.collect { it.testID }.join(', ')
println testCase
There are other ways to represent this in JSON, but you should study how JSON works and what suits you best depending on how it is going to be used.
Here's the JSON spec which is simple enough you can learn it in 20 minutes:
https://www.json.org/json-en.html

Can I return response before task is done?

I have simple flask api. Part of the code is bellow.
#app.route("/newTask")
def task():
new_task = Task()
asyncio.run(new_task.do_something()) # something like this?
return f'New task with number {new_task.id} created'
#app.route("/tasks")
def task_status():
response = {task.id: task.status for task in TasksTable}
return jsonify(response)
When I make a request to http://something/newTask I would like to immediately see response "New task with number X created".
And then at /tasks endpoint observe this:
{
"1": "initialized",
}
After refresh this:
{
"1": "in progress",
}
And after some time this:
{
"1": "done",
}
Is it possible without celery? Maybe using asyncio?

How do i test except Errors using side_effect in python unittest mock?

I am a junior developer and trying to write some unittests for our API endpoints. Below is the class that i am testing and the actual test that runs without any issue. (But i am still hesitant that it's hitting my methods). My question is how can i improve my test and also make sure it covers exception(in this case ValueError, SystemError, Exception) by using side_effects(or any better suggestions) from python mock? I read python mock documentation but still can't figure out how to improve and importantly test exceptions.
we use flask microframework, python3.x,
--- Class that i am testing:
#USER_MOD.route('', methods=[HttpMethods.HTTP_POST])
#jwt_required
def create_user():
"""
Private end point for creating users of the system
:return: json
"""
response = ""
try:
logger_obj.info("Request : POST : Create User: {} ".format(request.base_url))
# validating input request
if ValidationUtils.validate_request(request, "CREATE_USER"):
logger_obj.debug("Request Validation Successful")
response = UserService().create_new_user(request)
logger_obj.debug("User Created: {} ".format(response))
return jsonify(response), HTTPStatus.OK
except ValueError as error:
logger_obj.error("ValueError create_user() : {}".format(error))
response = ApplicationUtils.create_api_response(status=ResponseConstants.MSG_ERROR, message=str(error))
return jsonify(response), HTTPStatus.BAD_REQUEST
except SystemError as error:
logger_obj.error("SystemError create_user() : {}".format(error))
response = ApplicationUtils.create_api_response(status=ResponseConstants.MSG_ERROR, message=str(error))
return jsonify(response), HTTPStatus.INTERNAL_SERVER_ERROR
except Exception as error:
logger_obj.error("Exception create_user() : {}".format(error))
response = ApplicationUtils.create_api_response(status=ResponseConstants.MSG_ERROR, message=str(error))
return jsonify(response), HTTPStatus.UNAUTHORIZED
finally:
logger_obj.info("Response : POST : Create User: {} : {}".format(request.base_url, response))
--- Test for above class:
class UserApiTests(DataTestCase): //(or i can use unittest.TestCase)
def setUp(self):
os.environ.update(self.config.to_env())
self.flask_app = make_flask_app()
self.client = self.flask_app.test_client()
self.flask_app.testing = True
#patch('usermgmt.app.services.user_service.UserService.create_new_user')
def test_create_user(self, mock_create_new_user):
# Given
mock_create_new_user.return_value.status_code = 200
mock_create_new_user.return_value = {
"status": "SUCCESS"
}
data_to_post = {
"name": "Test2",
"email": "new-user2#entity.com",
"entity_id": 1,
"is_active": True,
"product_roles": [
{"product_id": 1, "role_id": 4},
{"product_id": 2, "role_id": 4}
],
}
# When
response = self.client.post('/api/usermgmt/users', data=json.dumps(data_to_post), headers={
"Authorization": "Bearer {}".format(get_jwt(identity=self), "Content-Type: application/json")
})
data = response.data
json_data = json.loads(data)
# Then
self.assertEqual(response.status_code, 200)
self.assertEqual(json_data['status'], "SUCCESS")
During my practice I've found that approach quite good.
class TestCreateUser(TestCase):
def test_works_in_correct_case(self):
# equal to your test_create_user
#patch("usermgmt.app.services.user_service.UserService.create_new_user")
def test_handles_value_error_in_create_new_user(self, mock_create_new_user):
mock_create_new_user.side_effect = ValueError
# Your preparation flow here
response = self.client.post('/api/usermgmt/users') # simplified call here is just an example
self.assertEqual(response.status_code, 400)
# check if response body is what you've expected, etc.

Resources