I have some Django DRF tests that look like this:
from django.conf import settings
from rest_framework.test import APILiveServerTestCase, RequestsClient
class APITests(APILiveServerTestCase):
def setUp(self):
self.client = RequestsClient()
def test_endpoints(self):
with self.settings(MEDIA_ROOT=f"{settings.BASE_DIR}/test_media/"):
# Upload file
self.client.post(
URL,
files={"my_file": ("filename", b"abc")},
)
# Download the file we just uploaded
response = self.client.get(
URL_to_FILE,
)
The upload works fine, but the download fails with a 404, because the test webserver isn't serving media files, and more specifically, not serving them from my custom MEDIA_ROOT=f{settings.BASE_DIR}/test_media/ folder.
Adding entries to urls.py to have Django serve media files didn't work.
Django appears to have a TestCase subclass specifically designed for this: django.contrib.staticfiles.testing.StaticLiveServerTestCase. DRF doesn't have anything like this, but I figured I could make one like this:
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from rest_framework.test import APIClient
class APIStaticLiveServerTestCase(StaticLiveServerTestCase):
client_class = APIClient
class APITests(APIStaticLiveServerTestCase):
....
This didn't work either.
Is there a way to make this work?
Related
I am testing loginradius for authentication in a fastapi app but cant seem to get the redirect or modal form to work correctly. I have tried using the OAuth2PasswordBearer and OAuth2AuthorizationCodeBearer classes but neither worked as desired. I would like to be able to click the "Authorize" button and either get redirected to the loginradius login page https://<app_name>.hub.loginradius.com (and returned to the api page with the token from loginradius) or render the loginradius login/registration form instead of the fastapi OAuth2PasswordBearer form. example:
Instead of
Desired outcome
Small Code Snippet
"""Fastapi routes for all User related endpoints"""
from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordBearer
router = APIRouter()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# oauth2_scheme = OAuth2AuthorizationCodeBearer(
# tokenUrl="https://<app_name>.hub.loginradius.com",
# authorizationUrl="https://<app_name>.hub.loginradius.com")
#router.post("/me", response_model=None)
def create_me(token: str = Depends(oauth2_scheme)):
return {"token": token}
Is that possible?
Thanks
I have this test for checking if I can ping the swagger endpoint
from django.test import SimpleTestCase
from django.test.utils import override_settings
from django.urls import reverse
from rest_framework import status
class SwaggerTest(SimpleTestCase):
#override_settings(DEBUG=True)
def test_successful_swagger_ping(self):
"""
Test to ensure that Swagger can be reached successfully. If this test
throws 5XX that means there's an error in swagger annotation on some view function.
"""
response = self.client.get(reverse('swagger'))
self.assertEqual(response.status_code, status.HTTP_200_OK)
So this test fails, because I only have swagger added to url when settings.DEBUG=True. I thought #override_settings(DEBUG=TRUE) would fix that, but it doesn't. My test passes when I manually set settings.DEBUG=True under my settings.py file, otherwise it throws an error because it can't find the endpoint. I've tried decorating both the class and the function with #override_settings and in both cases it threw the error because it couldn't find the endpoint. I'm not particularly attached to this way of testing to see if Swagger is working. This test is just to check if Swagger isn't returning an error regarding annotation. If anyone knows a better way to test this I'm open to that.
I've also tried
from django.conf import settings
from django.test import TestCase
from django.urls import reverse
from rest_framework import status
class SwaggerTest(TestCase):
#staticmethod
def setUpClass() -> None:
settings.DEBUG = True
super(SwaggerTest, SwaggerTest).setUpClass()
def test_successful_swagger_ping(self):
"""
Test to ensure that Swagger can be reached successfully. If this test
throws 5XX that means there's an error in swagger annotation on some view function.
"""
response = self.client.get(reverse('swagger'))
self.assertEqual(response.status_code, status.HTTP_200_OK)
but that also gives django.urls.exceptions.NoReverseMatch: Reverse for 'swagger' not found. 'swagger' is not a valid view function or pattern name. it only works when I set
test/__init__.py
settings.DEBUG = True
Is it ok to change settings?
Short answer: No, unless you do it during the startup.
Long answer: You should not modify settings at runtime. This means, no settings modifications after the app has been started, like changing configuration in views.py, serializers.py, models.py or other modules you add during the development. But it is OK to modify settings if they depend on local variables if you do it at startup and you are fully aware of what happens.
But this is how I usually override settings in tests:
from django.conf import settings
from django.test import TestCase
class MyTestCase(TestCase): # noqa
#staticmethod
def setUpClass(): # noqa
settings.DEBUG = True
super(ExampleTestCase, ExampleTestCase).setUpClass()
But maybe you should run your tests twice for that?
I am running unittests in a Flask app and I keep getting 404 when views.py file is not imported even though it is not used. I have such tests.py package:
import unittest
from presence_analyzer import main, utils
from presence_analyzer import views
class PresenceAnalyzerViewsTestCase(unittest.TestCase):
def setUp(self):
self.client = main.app.test_client()
def test_mainpage(self):
resp = self.client.get('/')
self.assertEqual(resp.status_code, 302)
When I delete views import the described problem occurs. Views are organized in a similar way to this:
from presence_analyzer.main import app
#app.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And the main.py file:
import os.path
from flask import Flask
app = Flask(__name__) # pylint: disable=invalid-name
app.config.update(
DEBUG=True,
)
I guess it's something similar to what happened in this case, so I'm trying to change the application so that I don't have to make this dumb imports while testing. I've been trying to make use of the answer from above, but still can't make it work and these docs don't seem helpful. What am I doing wrong? main.py:
from flask.blueprints import Blueprint
PROJECT_NAME = 'presence_analyzer'
blue_print = Blueprint(PROJECT_NAME, __name__)
def create_app():
app_to_create = Flask(__name__) # pylint: disable=invalid-name
app_to_create.register_blueprint(blue_print)
return app_to_create
app = create_app()
views.py:
from presence_analyzer.main import app, blue_print
#blue_print.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And tests.py has remained unchanged.
You must import views, or the route will not be registered. No, you are not executing the views directly, but importing executes code all module-level code. Executing code calls route. route registers the view function. You cannot get around needing to import a module in order to use the module.
I need to rewrite an existing WebService as part of a Django application, the goal is to integrate the Django application to a legacy system that can just call this particular WebService.
So I do have a very complex WSDL file with several methods and a huge data structure. Is there any way to generate a stub for an application in Django using that WSDL file, or do I have to create all the necessary data structures and method signatures myself?
Check out Zeep
Zeep inspects the WSDL document and generates the corresponding code to use the services and types in the document.
views.py (Server)
from django.http import HttpResponse
from django.views import View
from zeep import Client
class MyConvertKilometer2MilesView(View):
def get(self, request):
# <view logic>
client = Client('http://www.webservicex.net/ConvertSpeed.asmx?WSDL')
result = client.service.ConvertSpeed(
100, 'kilometersPerhour', 'milesPerhour')
return HttpResponse('result')
If you are not sure how fast you get returns (due to the nature of SOAP), use a
dango-channels tasks or
celery tasks
Command Line tests with Zeep
If you like to test something inbetween, you may us the command line interface of Zeep to make this easy.
python -mzeep http://www.soapclient.com/xml/soapresponder.wsdl
The "Client" class from the "zeep" library is used to create a client object that will connect to the specified WSDL endpoint, "http://www.webservicex.net/ConvertSpeed.asmx?WSDL". This client object is then used to call the "ConvertSpeed" method of the web service, passing in the parameters "100", "kilometersPerhour", and "milesPerhour".
views.py (Client)
A Django example that generates a client view would be:
from django.http import HttpResponse
from django.views import View
from zeep import Client
class MyConvertKilometer2MilesView(View):
def get(self, request, speed):
client = Client('http://www.webservicex.net/ConvertSpeed.asmx?WSDL')
result = client.service.ConvertSpeed(speed, 'kilometersPerhour', 'milesPerhour')
return HttpResponse(result)
You also need to update the routing configuration in urls.py file to include this parameter in the url path.
from django.urls import path
from .views import MyConvertKilometer2MilesView
urlpatterns = [
path('convert/<int:speed>/', MyConvertKilometer2MilesView.as_view()),
]
Currently I have a Django project, let's call it Backend. There is a folder api and there I have this resource declared using Django-Tastypie:
from django.contrib.auth.models import User
from tastypie.resources import ModelResource, ALL
from tastypie.authentication import BasicAuthentication
from tastypie.authorization import DjangoAuthorization
from tastypie.cache import SimpleCache
from tastypie.throttle import CacheDBThrottle
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'user'
excludes = ['email', 'password', 'is_staff', 'is_superuser']
authentication = BasicAuthentication()
authorization = DjangoAuthorization()
cache = SimpleCache()
throttle = CacheDBThrottle()
filtering = {
'username' : ALL,
'date_joined' : ['gt','lt','gte','lte','range']
}
With proper routing rules in place, if I access an URL like http://127.0.0.1:8000/api/v1/user?format=json, I am supposed to get back some info on users in json format, but from my local database. I do not want to use any local database, except maybe for testing with some dummy data. I want such a get request to result in a SOAP request to a certain remote server with my logged in session's username and password.
I already have a standalone Python application, where I can perform a SOAP request and get a SOAP response back using SUDS and pre-downloaded WSDL files. Now I want to include this functionality in my Dhango project in such a way, that I change the settings in settings.py in the project, but I do not have to modify the applications inside the project.
You can define your own Custom managers that use your standalone Python application.
From tastypie documentation. Adding this code to your urls.py should work:
from tastypie.api import Api
from my_app.api.resources import UserResource
v1_api = Api(api_name='v1')
v1_api.register(UserResource())
#old urlpatterns here
urlpatterns += patterns('',
(r'^api/', include(v1_api.urls)),
)