Django : How can I mock request.session of view? - python

I'm using mocking to test views.
tests.py
#patch('orders.views.OrderView.generate_merchant_uid')
def test_expected_price_is_registered_on_GET_request(self, mock_generate_merchant_uid):
self.client.get(reverse('orders:order'))
views.py
class OrderView(LoginRequiredMixin, View):
def generate_merchant_uid(self):
merchant_uid = "blah_blah_blah"
return merchant_uid
def get(self, request, *args, **kwargs):
merchant_uid = self.generate_merchant_uid()
request.session['merchant_uid'] = merchant_uid
return HttpResponse('a')
It occurs errors:
TypeError: <MagicMock name='generate_merchant_uid()' id='4431843456'> is not JSON serializable
It occurs error because I mocked generate_merchant_uid and it returns MagicMock and View trying to store this MagicMock in the request.session.
I think what I have to do is to mock request.session.
But have no idea how I can do that.
Need advices. Thanks.

The problem is not about mocking the session itself. You forgot to set what your mocked function should return. By default it returns a Mock object and it is trying to store it request session and converting it to JSON, there is where you got the error, Mock instance is not JSON serializable.
#patch('orders.views.OrderView.generate_merchant_uid')
def test_expected_price_is_registered_on_GET_request(self, mock_generate_merchant_uid):
mock_generate_merchant_uid.return_value = //here goes your mocked value
self.client.get(reverse('orders:order'))
For example:
mock_generate_merchant_uid.return_value = "blah_blah_blah"

Related

Python patch context manager to return object

I am trying to patch a context manager that does a database lookup and returns an object like follows:
class MyClass:
#contextlib.contextmanager
def client_ctx(self, id):
# hidrate from DB and yield object
yield client # instance of SQAlchemy model Client
def run(self, id):
with self.client_ctx(id) as cl:
# do work here
Client class in this case is a SQLAlchemy model.
In my tests I am trying to patch this method client_ctx to simply return an object instantiated in the tests like this:
#patch('MyClass.client_ctx')
def test_ctx(self, _mocked_ctx_manager):
myclass = MyClass()
client = Client(
id=1,
code='test-client')
_mocked_ctx_manager.__enter__.return_value = client
myclass.run(1)
I'm getting: TypeError: Object of type MagicMock is not JSON serializable which makes no sense to me. What am I doing wrong, is there a better way to mock a context manager ?
The following should work:
_mocked_ctx_manager.return_value.__enter__.return_value = client
Your _mocked_ctx_manager returns a context manager. Therefore you need to set the __enter__.return_value of _mocked_ctx_manager.return_value.
I found the following article to be helpful: Surrender Python Mocking! I Have You Now.

Calling post a method of a serializer and passing data to it

I want to call a post of a class as shown below by instantiating a view as shown below., i need to write a unit test that calls the post method of this class and not through URL.
class SequencingRequestSpreadsheetView(GenericAPIView):
parser_classes = (JSONParser,)
serializer_class = SequencingRequestSerializer
permission_classes = (IsBioUser, )
suffix = '.xls'
path = settings.SEQUENCE_REQUEST_SUBMISSION
def post(self, request, format=None, simulation_mode = False):
I need to know how do I create a request object and pass it to this function.
iam instantiating this view class and I tried passing a request data as json and also tried dictionary but did not work.
how do I create a request object and pass it to this method.
resp = SequencingRequestSpreadsheetView().post(request)
You can use RequestFactory for achieve what you want.
factory = RequestFactory()
# Build a post request.
request = factory.post(post_url, data, ...)
# Note here that, I don't call the class view directly
# with:
# SequencingRequestSpreadsheetView().post(request)
# instead I get the view with as_view(), and then pass
# a post request to it.
view = SequencingRequestSpreadsheetView.as_view()
response = view(request, ...)
See Making requests here to get a better understanding on how RequestFactory works.

How to mock an external api in django?

I'm trying to mock the "self.api.friends.get" method in VKAuth class:
import vk
class VKAuth(object):
def __init__(self, access_token, user):
self.session = vk.Session(access_token = access_token)
self.api = vk.API(self.session)
def follow(self):
vk_friends = self.api.friends.get()
from the test module test_views.py:
from mock import patch
from ..auth_backends.vk_backend import VKAuth
class AddUsersToList(TestCase):
#patch.object(VKAuth.api.friends, 'get')
def test_auth_vk(self, mock_get):
... etc ...
And I get an error during testing:
AttributeError: <class 'accounts.auth_backends.vk_backend.VKAuth' doens't have the attribute 'api'
What am I doing wrong? How to get an access to this method in this class structure?
You're trying to mock a class itself, not it's instance. And the class doesn't have the api attribute, as it's created in your __init__(). Change your code to:
def test_auth_vk(self, mock_get):
vk_auth = VKAuth(access_token, user)
with mock.patch('vk_auth.api.friends') as friends_mock:
friends_mock.get.return_value = None
# Invoke the code that calls your api, passing the "vk_auth" variable as a backend.
# ...
friends_mock.mock.get.assert_called_with(your_arguments)
If you can't just pass an auth backend to your code, look up the place where it is instantiated and mock that place.

Django Rest Framework: How can I patch a serializer for unit testing, when it's used in the serializer_class attribute?

I have a serializer with an create() function. When I do a POST request, then I want to get this function called and create a new object. When I do it in the browser, it works and it also calls the function. But inside the test, it says that the function is not called. I think that I have done something wrong with the patch, because in the API it is only set as an serializer_class and the class is likely called somewhere inside the framework. Another thought was, that I do not need to test this, because it should be guaranteed by the rest_framework, that if I do it this way, the framework should call the function with the correct parameters.
# serializers.py
class FooSerializer(models.ModelSerializer):
class Meta:
...
def create(self, validated_data):
...
# apis.py
class FooAPI(generics.CreateAPIView):
serializer_class = FooSerializer
# tests.py
#patch('apis.FooSerializer'):
def test_that_create_is_called(self, mock):
mock.create = MagicMock()
mock.create.return_value = Foo() # Foo is my model
response = self.client.post('/foo', {name: 'Test'})
self.assertTrue(mock.create.called) # => Output says "False is not true"
Your current code is mocking the entire Serializer object, which is probably overkill and could stop the create method ever being called if it's expected to be called by internal logic on the serializer.
Instead you want to just patch a single method - like this:
#patch('apis.FooSerializer', 'create')
Now your test method receives the MagicMock object instance that has replaced the create method.
So your test method becomes:
def test_that_create_is_called(self, mock_method):
response = self.client.post('/foo', {name: 'Test'})
self.assertTrue(mock_method.called)

pyramid_formalchemy assumptions about requests

I'm using pyramid_formalchemy 0.4.1...well I'm trying to use it.
When requests come in to my app I keep seeing pyramid_formalchemy making strange assumptions about what will be in the request object. My requests fail because in pyramid_formalchemy.views (starting at line: 58) the ModelView class has a constructor with the following code:
def __init__(self, context, request):
self.context = context
self.request = request
self.session = request.session_factory
self.fieldset_class = request.forms.FieldSet
self.grid_class = request.forms.Grid
The thing is my request object has a 'session' not a 'session_factory'. It also doesn't have 'forms'
Here is what I've done:
Create a RootFactory that extends pyramid_formalchemy.resources.Models
I call config.formalchemy_admin() passing it my RootFactory and my DBSession
I created an empty forms.py file.
What am I missing in my setup? Any ideas?
Thanks.
This stuff is configurable.
See the source
Not sure it's really documented..

Categories