I have a function that makes an http request and throws an error if the response is not a 200. It looks like this:
def put_request(param1, param2):
url = f"api/v1/some/route"
response = requests.put(
url,
json=param2,
verify="test",
)
if response.status_code != 200:
raise CustomError()
return response.json()
I want to test that the exception is correct so my test code looks like:
def test_put_request_error(mocker):
requests_mock = mocker.patch("path.to.file.requests")
requests_mock.put.return_value.status_code = 500
with pytest.raises(CustomError) as error:
put_request(param1=param1, param2={some data})
assert error.value.code == 500
Problem is, the error gets raise in the code gets raised and execution stops. It never makes it back to the assertion in the my test. I could use some advice on fixing this!
This pattern seems to work for my other test cases so I'm not sure what the problem here is!
EDIT: The issue was in the imports! The imports from my tests needed to be the same from my actual code. This means both need to be absolute or relative!
The issue here was in the imports. After MrBean Bremen asked to show the imports, I realized my code used relative paths and my test used absolute paths. When I made these both the same they worked!
Related
I have to use a try and except block with the following code, as I am trying to get a model class object but in case if database is empty so for that I need to use try and except.
if(txStatus=='SUCCESS'):
order=Order.objects.get(id=id) #NEED TRY ACCEPT BLOCK FOR THIS
URL = payment_collection_webhook_url
request_data ={}
json_data = json.dumps(request_data)
requests.post(url = URL, data = json_data)
return Response(status=status.HTTP_200_OK)
try..except..else..finally block works like this:
try:
order=Order.objects.get(id=id)
except ObjectDoesNotExist(or MultipleObjectsReturned in case it returns more
than 1 queryset or instance):
...Handle the exception, Write any code you want
else:
...This gets called if there is no exception or error, means it will
execute right after try statement if there's no error, so if you want
something more to happen only if the code doesn't throw an error, you can
write it here
finally:
...This gets executed no matter what in any case, means if there's
something you want to execute regardless of whether it throws an
exception or not, you write it here.
It is as simple as this:
try:
order = Order.objects.get(id=order_id)
except Order.DoesNotExist:
# Exception thrown when the .get() function does not find any item.
pass # Handle the exception here.
You can find more information about DoesNotExist exception here.
#Hagyn is correct, but in Django there is another way to do that:
Something like this:
orders = Order.objects.filter(id=order_id)
if orders.exists():
order = orders.last()
else:
# do rest of the things
Let's say I want to display my own 404 & 500 pages, I've found 2 possibilities so far:
1: Using cherrypy.config.update
def error_page_404(status, message, traceback, version):
return ('Error 404 Page not found')
def error_page_500(status, message, traceback, version):
return ('Error:')
cherrypy.config.update({'error_page.404': error_page_404, 'error_page.500': error_page_500})
Using _cp_config:
from cherrypy import _cperror
def handle_error():
cherrypy.response.status = 500
cherrypy.log("handle_error() called. Alarm!", "WEBAPP")
cherrypy.response.body = ['Sorry, an error occured. The admin has been notified']
error = _cperror.format_exc()
def error_page(status, message, traceback, version):
cherrypy.log("error_page() called. Probably not very important.", "WEBAPP")
return "Sorry, an error occured."
class Root:
_cp_config = {
'error_page.default': error_page,
'request.error_response': handle_error
}
but is there a difference or a suggestion which is preferable to use?
request.error_response allows you to set a handler for processing of some unexpected errors, like your own exceptions raised from HTTP handlers.
The callable that you'll set for this option will receive no arguments at all and you'll have to inspect sys.exc_info() for the details, to find out what happened.
You'll also have to set cherrypy.response.status and cherrypy.response.body by yourself, explicitly in your error handler.
If you want to modify the error response for HTTP error codes (when instances of cherrypy.HTTPError are raised, like raise cherrypy.NotFound), you can use error_page.default (catch-all) or error_page.404 (error-specific) for handling those errors.
error_page options support both file path and callable values. In case of using a file path, the HTML template file can use the following substitution patterns: %(status)s, %(message)s, %(traceback)s, and %(version)s.
If you opt-in to using a function, it'll receive those as arguments (callback(status, message, traceback, version)). The return value of this callable is then used HTTP response payload.
As you can see, these approaches have different implications and different levels of flexibility and usability. Choose whatever works for you. Internally, the default request.error_response uses error_page settings to figure out what to return. So if you redefine request.error_response, it'll not use error_page.* settings unless you explicitly make it do so.
See the docstring with some explanation here.
I'm building a helper library to call the AdWords (Google Ads) Keyword Planner API and having trouble catching RateExceededError errors when they come up.
The specific error message that I'm getting is below.
GoogleAdsServerFault: RateExceededError <rateName=RATE_LIMIT, rateKey=null, rateScope=ACCOUNT, retryAfterSeconds=30, errorDetails="Quota check failed: QuotaInfo{quotaGroupId=v2-kwi-webapi-global, userId=global}"> Original AdsAPI trace for debugging [
com.google.ads.api.services.common.error.ApiException: RateExceededError <rateName=RATE_LIMIT, rateKey=null, rateScope=ACCOUNT, retryAfterSeconds=30, errorDetails="Quota check failed: QuotaInfo{quotaGroupId=v2-kwi-webapi-global, userId=global}">
Underlying ApiErrors are:
RateExceededError <rateName=RATE_LIMIT, rateKey=null, rateScope=ACCOUNT, retryAfterSeconds=30>
I'm currently working with the below setup to call the API and catch errors, however exceptions are still being raised occasionally. Is there a better way I should catch these errors and just log the exceptions as warnings?
class AdwordsAPIException(Exception):
pass
def call_adwords_api_client(self, selector):
try:
return _adwords_client.get(selector)
except AdwordsAPIException:
return None
Many thanks in advance!
Well, you have made a custom Exception class which is never raised, to skip all exceptions try this
def call_adwords_api_client(self, selector):
try:
return _adwords_client.get(selector)
except:
return None
Also, api suggests to wait for 30 seconds before trying again. good luck.
I have code as follows:
try:
schema = lxml.etree.RelaxNG(file=schema_file)
schema.assertValid(etree)
except lxml.etree.DocumentInvalid as exception:
print(schema.error_log)
print(exception.error_log)
raise exception
It consistently raises a DocumentInvalid error:
DocumentInvalid: Document does not comply with schema
yet prints no errors in either the schema error log or the global error log.
This only happens for some files, others validate correctly, or give a reason for failing to validate. (It's a very long schema, so I won't quote it here.)
I don't even know where to start looking. What could the cause possibly be?
That is how you can get an error message
try:
schema = lxml.etree.RelaxNG(file=schema_file)
schema.assertValid(etree)
except lxml.etree.DocumentInvalid as exception:
print(exception.error_log.filter_from_errors()[0])
raise exception
I have a unit test that's failing in an assertion that passes in another test in the same test case class.
Here's the passing test:
def test_home(self):
c = Client()
resp = c.get('/')
self.assertEqual(resp.status_code, 200)
self.assertTrue('a_formset' in resp.context)
Here's the failing test:
def test_number_initial_number_of_forms(self):
c = Client()
resp = c.get('/')
self.assertEqual(resp.context['a_formset'].total_form_count(), 1)
In the second test, I get the error TypeError: 'NoneType' object has no attribute '__getitem__'.
If I execute the second test as
def test_number_initial_number_of_forms(self):
c = Client()
resp = c.get('/')
self.assertTrue('a_formset' in resp.context)
self.assertEqual(resp.context['a_formset'].total_form_count(), 1)
I get the error TypeError: argument of type 'NoneType' is not iterable. I've confirmed via print statements in the second test that the response.content contains the page I expect to get, that the status code is correct, and that the template is correct. But the response's context is consistently None in the second test.
I'm running my Django unit tests through the standard "python manage.py test ..." interface, so I don't believe I'm running into the "context is empty from the shell" issue.
What's going on with this?
Edit:
If I add print type(resp.context['a_formset']) to each test, for the working test I get <class 'django.forms.formsets.AFormFormSet'>. For the non-working test, I get TypeError: 'NoneType' object has no attribute '__getitem__' again.
It's because you ran into some error, exited the shell and restarted it.
But you forgot to start environment...
from django.test.utils import setup_test_environment
>>> setup_test_environment()
That was my problem. Hope it works...
Today I run into the same issue. The second test gets same page has nothing in response.context
I made a research and found that
1) test client uses signals to populate context,
2) my view method is not called for the second test
I turned on a debugger and found that the guilty one is 'Cache middleware'. Knowing that I found this ticket and this SO question (the latter has a solution).
So, in short: the second request is served from cache, not from a view, thus a view is not executed and test-client doesn't get the signal and have no ability to populate context.
I can not disable cache middleware for my project, so I added next hack-lines into my settings:
if 'test' in sys.argv:
CACHE_MIDDLEWARE_SECONDS = 0
Hope this helps someone
You can also clear cache manually by calling cache.clear() inside a test method:
from django.core.cache import cache
import pytest
class TestPostView:
#pytest.mark.django_db(transaction=True)
def test_index_post(self, client, post):
cache.clear()
response = client.get('/')