I would like to patch an attribute of the data returned by a method.
Assuming I have the following simplified piece code:
#patch('requests.post')
class TestKeywordsApi(BaseTest):
# Instantiate API class and set the apikey
def setUp(self):
BaseTest.setUp(self)
self.fixtures = FIXTURES
self.api = BaseApi()
def mock_requests_post(self, url, data=None):
''' Mock method for post method from responses library.
It replaces the responses.post calls in Api class.
'''
url = self.encode_url(url, data)
if url:
return self.fixtures[url]
def test_save_success(self, mock_post):
mock_post.side_effect = self.mock_requests_post
response = self.api.post(keyword, params={...})
# list of asserts
# original class calling requests.post
import requests
class BaseApi(object):
def post(self, action, params):
''' Sends a POST request to API '''
response = requests.post(self.build_url(action), data=params).content
The code above fails because the mock method does not provide a mock/stub for 'content' attribute present in requests library. Does anyone know how to stub the content attribute?
your mocked post function needs to return an object that is more like requests's response objects, one that has a .content attribute. eg:
from mock import Mock, patch
#[...]
def mock_requests_post(self, url, data=None):
''' Mock method for post method from responses library.
It replaces the responses.post calls in Api class.
'''
mock_response = Mock()
mock_response.content = 'my test response content'
url = self.encode_url(url, data)
if url:
mock_response.url_called = self.fixtures[url]
return mock_response
I found the following solution, which only modifies the mock_requests_post method, adding an internal class with the attribute that I need:
def mock_requests_post(self, url, data=None):
''' Mock method for post method from responses library.
It replaces the responses.post calls in Api class.
'''
url = self.encode_url(url, data)
class classWithAttributes(object):
content = json.dumps(self.fixtures[url])
if url:
return classWithAttributes()
Related
im working on a python package and I wonder how can I declare a class, which receive some attributes in the init function and then be able to use that 'self' attributes in the rest of the functions without declaring self as it's parameters.
Here is an example code to make it easier:
class API():
def __init__(self, token):
self.token = token
def info():
headers = {'Token': f'{self.token}'}
response = requests.post(some_url, headers=headers)
return response
I didn't put self in info() function because that function is going to be called from the outside, but it will be great be able to reuse the token attribute received in the class initialization. i don't know if I'm missing something so any suggestion will be much appreciated.
Edit
If I use my current code, I get an error because using self keyword without declaring it on the function class, but if I put it, then when I make the function call I can pass self argument.
self is not a keyword; it's just a conventional name for the instance of API that is passed to info when it is called as an instance method.
You can't call info without such an instance.
class API():
def __init__(self, token):
self.token = token
def info(self):
headers = {'Token': f'{self.token}'}
response = requests.post(some_url, headers=headers)
return response
a = API("some token")
a.info()
a.info() is roughly equivalent to API.info(a).
I'm not sure what's the proper way to test functions which are used inside views/permission classes.
This is the payload of my request:
{"name": "John"}
And this is the function I want to test:
def get_name(request):
return request.data['name']
This is the view that will be using the function:
class SomeView(APIView):
def get(self, request):
name = get_name(request=request)
return Response(status=200)
How should I create a fixture to test the get_name function? I've tried this:
#pytest.fixture
def request_fixture()
factory = APIRequestFactory()
return factory.get(
path='',
data={"name": "John"},
format='json')
def test_get_name(request_fixture):
assert get_name(request=request_fixture) == "John"
But I'm getting an error:
AttributeError: 'WSGIRequest' object has no attribute data.
One workaround seems to be decoding the body attribute:
def get_name(request):
data = json.loads(request.body.decode('utf-8'))
return data['name']
But it doesn't feel like the right way to do this and I guess I'm missing something about the WSGIRequest class. Can someone explain to me how it should be tested? It would be great if I could use the same fixture to test the view too.
I don't think you need the test fixture. You aren't testing the whole view, just a helper function. You can make a request-like object very easily by adding a property to a lambda:
def test_get_name():
request = lambda: None
request.data = {"name": "John"}
assert get_name(request=request) == "John"
I am struggling with mocking attributes in my Python tests. The function I am trying to test keeps failing because the mock probably returns the right value but is the wrong type (Should be string and it is a MagicMock instead.
I have found this answer and I understand I need to use a PropertyMock. But I can't get it to work neither with the context manager or using the #patch decorator. Mock attributes in Python mock?
Here is my test:
#patch('keys.views.requests.post')
#patch('keys.views.requests.Response.text', new_callable=PropertyMock)
def test_shows_message_when_receives_error(self, mock_response_text ,mock_post):
expected_error = escape(MESSAGE)
data_to_be_received = json.dumps({
"message":"Bad Request",
"errors":[{
"resource":"Application",
"field":"client_id",
"code":"invalid"
}]
})
mock_response_text.return_value = data_to_be_received
response = self.client.get('/users/tokenexchange?state=&code=abc123')
self.assertContains(response, expected_error)
And the code I am testing:
def token_exchange(request):
parameters = {'client_id': '##', 'client_secret': '##', 'code': code}
response = requests.post('https://www.strava.com/oauth/token', parameters)
data_received = json.loads(response.text)
if 'errors' not in data_received:
return HttpResponse(response.text)
else:
return render(request, 'home.html', {'error': STRAVA_AUTH_ERROR})
The error I keep getting is:
File "##", line 66, in token_exchange
data_received = json.loads(response.text)
TypeError: the JSON object must be str, bytes or bytearray, not 'MagicMock'
Thanks for your answers!!!
keys.views.requests.Response.text is highly likely a instance variable which cannot and should not be mocked using PorpertyMock
Here is quote from documentation:
Generally speaking, instance variables are for data unique to each instance and class variables are for attributes and methods shared by all instances of the class:
https://docs.python.org/2/tutorial/classes.html
class Dog:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
How to mock a python class instance_variable?
I had a solution which copied from somewhere, it worked by too tedious:
Python mock a base class's attribute
In your specific case, mock Response class instead of the text instance
#patch('keys.views.requests.post')
#patch('keys.views.requests.Response')
def test_shows_message_when_receives_error(self, mock_response ,mock_post):
mock_post.return_value = None # mock the func
mock_response.text = mock.Mock(text=data_to_be_received).text
I am trying to create a rest api client for talking to one of our services. Every request needs to include an authorisation header which is compromised of Epoch times, request verb, data, path etc.
I'm trying to use the python requests module as seamlessly as possible, but am unsure the best way to "inject" a header into every request.
There seems to be a concept of "hooks" in requests, but currently there is only a "response" hook.
I was considering extending the Session object and overriding the "send" method, adding the header and then passing it up to the super (Session.send) method.
My Python isn't fantastic when it comes to OOP and Inheritance, but this is what I have tried
class MySession(Session):
def __init__(self, access_id=None, access_key=None):
self.access_id = access_id
self.access_key = access_key
super(MySession, self).__init__()
def send(self, request, **kwargs):
method = request.method
path = urlparse(request.url).path
request.headers['Authorization'] = self.__create_security_header(method, path)
request.headers['Content-Type'] = "application/json"
return Session.send(self, request, **kwargs)
I guess you don't need to override the send method, since you have already overriden the __init__.
class MySession(Session):
def __init__(self, access_id=None, access_key=None):
super(MySession, self).__init__()
self.access_id, self.access_key = access_id, access_key
# provided __create_security_header method is defined
self.headers['Authorization'] = self.__create_security_header(method, path)
self.headers['Content-Type'] = "application/json"
Most likely that should do.
I am writing unit tests for a Django 1.4 app. In my tests.py, I would like to have a helper function that I can use in my test classes. The helper is defined as such:
def error_outcome(self, response):
self.assertEqual(response.status_code, 403)
data = json.loads(response._get_content())
self.assertEquals(data, {'error': 1})
Below is an example test class that uses the helper:
class SomeTest(TestCase):
def test_foo(self):
request = RequestFactory().post('/someurl')
response = view_method(request)
error_outcome(self, response)
This works, however it is not good because the helper should not be using self as it is a function, not a method. Any idea on how to make this work without self? Thanks.
Make a base test case class with the error_outcome() method defined:
class BaseTestCase(TestCase):
def error_outcome(self, response):
self.assertEqual(response.status_code, 403)
data = json.loads(response._get_content())
self.assertEquals(data, {'error': 1})
class SomeTest(BaseTestCase):
def test_foo(self):
request = RequestFactory().post('/someurl')
response = view_method(request)
self.error_outcome(response)