How to mock instance attribute of django form - python

I'm doing a unit test where I'm mocking a Django form, but I'm having some trouble because I need to mock two things from the form:
An instance attribute (token)
A method (is_valid)
I'm using the form in a view, importing it like this:
from profiles.forms import PaymentForm
And I have tried the following to mock it:
#patch('profiles.forms.PaymentForm')
def test_when_processing_a_payment_then_the_event_is_tracked(self, payment_form_class):
payment_form_class.is_valid.return_value = True
payment_form_class.cleaned_data = {'token': 1}
This approach does not work, is_valid returns false.
#patch('profiles.forms.PaymentForm')
def test_when_processing_a_payment_then_the_event_is_tracked(self, payment_form_class):
payment_form_class.return_value.is_valid = True
payment_form_class.return_value.cleaned_data = {'token': 1}
This neither.
I'm using Django and unittest. I have successfully mocked the is_valid with a helper function of our code base, but it does not seem to work with instance attributes. Any idea how to solve this?

You might need to mock the form on where it is used in your view since it's already imported there before your mock runs.
So something like:
#patch('my_app.my_views.PaymentForm')

Related

Correct use of pytest fixtures of objects with Django

I am relatively new to pytest, so I understand the simple use of fixtures that looks like that:
#pytest.fixture
def example_data():
return "abc"
and then using it in a way like this:
def test_data(self, example_data):
assert example_data == "abc"
I am working on a django app and where it gets confusing is when I try to use fixtures to create django objects that will be used for the tests.
The closest solution that I've found online looks like that:
#pytest.fixture
def test_data(self):
users = get_user_model()
client = users.objects.get_or_create(username="test_user", password="password")
and then I am expecting to be able to access this user object in a test function:
#pytest.mark.django_db
#pytest.mark.usefixtures("test_data")
async def test_get_users(self):
# the user object should be included in this queryset
all_users = await sync_to_async(User.objects.all)()
.... (doing assertions) ...
The issue is that when I try to list all the users I can't find the one that was created as part of the test_data fixture and therefore can't use it for testing.
I noticed that if I create the objects inside the function then there is no problem, but this approach won't work for me because I need to parametrize the function and depending on the input add different groups to each user.
I also tried some type of init or setup function for my testing class and creating the User test objects from there but this doesn't seem to be pytest's recommended way of doing things. But either way that approach didn't work either when it comes to listing them later.
Is there any way to create test objects which will be accessible when doing a queryset?
Is the right way to manually create separate functions and objects for each test case or is there a pytest-way of achieving that?

Django doesn't seem to mock model methods

I am trying to write some test cases for the following method as part of a model called Project:
def get_mouse_model_designs(self):
return {details.design.to_mouse_model()
for details in self.strategies.all()}
The trouble seems to be in the details.design.to_mouse_model() and I cannot seem to accurately mock this function. This is the test I have (self.details2 is the only model linked to the project in this test case, so it would be the only record returned by self.strategies.all()):
def test_mouse_model_designs_one_design(self):
mm_design = MagicMock()
self.details2.design.to_mouse_model = MagicMock(return_value=mm_design)
self.assertEqual(self.project2.get_mouse_model_designs(), {mm_design})
And here is the error message I get:
AssertionError: Items in the first set but not the second:
<MouseModel.LabWork.DesignTask.DesignTask object at 0x0A4B0910>
Items in the second set but not the first:
<MagicMock id='172651760'>
A MouseModel.LabWork.DesignTask.DesignTask object is what is returned by the to_mouse_model() method. But I mocked this out. So from the error message I can see that it is not actually mocking the to_mouse_model() method. I have tried to assert that the method was called and that fails also.
However, if I remove the to_mouse_model() in the function definition and update the test accordingly it passes.
Any help would be appreciated!
I was able to find a solution. By mocking the class method rather than the object method:
#patch("LabWork.models.Design.to_mouse_model")
def test_mouse_model_designs_one_design(self, mock_design_mm):
mm_design = MagicMock()
mock_design_mm.return_value = mm_design
self.assertEqual(self.project2.get_mouse_model_designs(), {mm_design})

Unittest sensitive_post_parameters decorator in django view

I have a view to create new users in my django project.
I am applying the #sensitive_post_parameters decorator to that view to make sure the password isn't logged if there is an unhandled exception or something like that (as indicated in the comments in the source code https://docs.djangoproject.com/en/2.0/_modules/django/views/decorators/debug/).
When I proceed to test the view, I would like to make sure that this protection of the sensitive information is still in place (that I didn't delete the decorator to the function by mistake or something).
I am aware, since the decorator is applied to my function, I can't test it directly from the view tests.
But, for example, with the #login_required decorator, I can test its effects with assertRedirects (as explained here How to test if a view is decorated with "login_required" (Django)).
I have been searching for a way to do that, but I can't find one that works.
I thought of something like this:
def test_senstive_post_parameters(self):
request = RequestFactory().post('create_user', data={})
my_sensitive_parameters = ['password']
self.assertEqual(
request.sensitive_post_parameters,
my_senstive_parameters
)
but that gives me an
AttributeError: 'WSGIRequest' object has no attribute 'sensitive_post_parameters'
Any help would be appreciated.
Even it is telling me I shouldn't be attempting to test this, though I would really like to, as it is seems like an important behaviour that I should make sure remains in my code as it is later modified.
You have created a request using RequestFactory, but you have not actually used it. To test the effect of your view you need to import the view and call it.
from myapp.views import create_user
def test_senstive_post_parameters(self):
request = RequestFactory().post('create_user', data={})
response = create_user(request)
my_sensitive_parameters = ['password']
self.assertEqual(
request.sensitive_post_parameters,
my_senstive_parameters
)

python pytest for testing the requests and response

I am a beginner to using pytest in python and trying to write test cases for the following method which get the user address when correct Id is passed else rises custom error BadId.
def get_user_info(id: str, host='127.0.0.1', port=3000 ) -> str:
uri = 'http://{}:{}/users/{}'.format(host,port,id)
result = Requests.get(uri).json()
address = result.get('user',{}).get('address',None)
if address:
return address
else:
raise BadId
Can someone help me with this and also can you suggest me what are the best resources for learning pytest? TIA
Your test regimen might look something like this.
First I suggest creating a fixture to be used in your various method tests. The fixture sets up an instance of your class to be used in your tests rather than creating the instance in the test itself. Keeping tasks separated in this way helps to make your tests both more robust and easier to read.
from my_package import MyClass
import pytest
#pytest.fixture
def a_test_object():
return MyClass()
You can pass the test object to your series of method tests:
def test_something(a_test_object):
# do the test
However if your test object requires some resources during setup (such as a connection, a database, a file, etc etc), you can mock it instead to avoid setting up the resources for the test. See this talk for some helpful info on how to do that.
By the way: if you need to test several different states of the user defined object being created in your fixture, you'll need to parametrize your fixture. This is a bit of a complicated topic, but the documentation explains fixture parametrization very clearly.
The other thing you need to do is make sure any .get calls to Requests are intercepted. This is important because it allows your tests to be run without an internet connection, and ensures they do not fail as a result of a bad connection, which is not the thing you are trying to test.
You can intercept Requests.get by using the monkeypatch feature of pytest. All that is required is to include monkeypatch as an input parameter to the test regimen functions.
You can employ another fixture to accomplish this. It might look like this:
import Requests
import pytest
#pytest.fixture
def patched_requests(monkeypatch):
# store a reference to the old get method
old_get = Requests.get
def mocked_get(uri, *args, **kwargs):
'''A method replacing Requests.get
Returns either a mocked response object (with json method)
or the default response object if the uri doesn't match
one of those that have been supplied.
'''
_, id = uri.split('/users/', 1)
try:
# attempt to get the correct mocked json method
json = dict(
with_address1 = lambda: {'user': {'address': 123}},
with_address2 = lambda: {'user': {'address': 456}},
no_address = lambda: {'user': {}},
no_user = lambda: {},
)[id]
except KeyError:
# fall back to default behavior
obj = old_get(uri, *args, **kwargs)
else:
# create a mocked requests object
mock = type('MockedReq', (), {})()
# assign mocked json to requests.json
mock.json = json
# assign obj to mock
obj = mock
return obj
# finally, patch Requests.get with patched version
monkeypatch.setattr(Requests, 'get', mocked_get)
This looks complicated until you understand what is happening: we have simply made some mocked json objects (represented by dictionaries) with pre-determined user ids and addresses. The patched version of Requests.get simply returns an object- of type MockedReq- with the corresponding mocked .json() method when its id is requested.
Note that Requests will only be patched in tests that actually use the above fixture, e.g.:
def test_something(patched_requests):
# use patched Requests.get
Any test that does not use patched_requests as an input parameter will not use the patched version.
Also note that you could monkeypatch Requests within the test itself, but I suggest doing it separately. If you are using other parts of the requests API, you may need to monkeypatch those as well. Keeping all of this stuff separate is often going to be easier to understand than including it within your test.
Write your various method tests next. You'll need a different test for each aspect of your method. In other words, you will usually write a different test for the instance in which your method succeeds, and another one for testing when it fails.
First we test method success with a couple test cases.
#pytest.mark.parametrize('id, result', [
('with_address1', 123),
('with_address2', 456),
])
def test_get_user_info_success(patched_requests, a_test_object, id, result):
address = a_test_object.get_user_info(id)
assert address == result
Next we can test for raising the BadId exception using the with pytest.raises feature. Note that since an exception is raised, there is not a result input parameter for the test function.
#pytest.mark.parametrize('id', [
'no_address',
'no_user',
])
def test_get_user_info_failure(patched_requests, a_test_object, id):
from my_package import BadId
with pytest.raises(BadId):
address = a_test_object.get_user_info(id)
As posted in my comment, here also are some additional resources to help you learn more about pytest:
link
link
Also be sure to check out Brian Okken's book and Bruno Oliveira's book. They are both very helpful for learning pytest.

Flask: Calling a class that takes a Resource

I have an endpoint that looks like:
api.add_resource(UserForm,'/app/user/form/<int:form_id>', endpoint='user_form')
My UserForm looks like:
class UserForm(Resource):
def get(self, form_id):
# GET stuff here
return user_form_dictionary
If I had a function called get_user_form(form_id) and I wanted to retrieve the return value from UserForm's get method based on the form_id parameter passed in. Is there a way in Flask that allows for some way to call UserForm's get method within the program?
def get_user_form(form_id):
user_form_dictionary = # some way to call UserForm class
# user_form_dictionary will store return dictionary from
# user_form_dictionary, something like: {'a': 'blah', 'b': 'blah'}
I'm not sure if there is a way to directly access the get method of the UserForm class from within your app, the only thing that springs to mind is to call the url for that resource but I don't recommend doing that.
Are you using the flask-restful extension by some chance? if so the below is based on the suggested intermediate project structure from there site here
In a common module (this contains functions that will be used throughout your application)
common\util.py
def get_user_form(form_id):
# logic to return the form data
Then in your .py that contains the UserForm class, import the util.py file from the common module then do the below
class UserForm(Resource):
def get(self, form_id):
user_form_dictionary = get_user_form(form_id)
# any additional logic. i try and keep it to a minimum as the function called
# would contain it. also this way maintanence is easier
return user_form_dictionary
Then somewhere else in your app after importing the common module you can reuse the same function(s).
def another_function(form_id):
user_form_dictionary = get_user_form(form_id)
# any additional logic.
# same rules as before
return user_form_dictionary
Fetch and display the data using Javascript's Fetch API.

Categories