Issue
I've got a Flask app with some routes protected by authlib's ResourceProtector.
I want to test that a route I've set up is indeed protected by a authlib.integrations.flask_oauth2.ResourceProtector
but I don't want to go to the trouble of creating a valid jwt token or anything like that. I want to avoid feeling like I'm testing authlib
, rather than testing my code.
All I really need to do is check that when I hit my endpoint, the ResourceProtector
is called. This should be possible with mocking of some kind, but I wonder if there's a supported way to do this.
from authlib.integrations.flask_oauth2 import ResourceProtector
require_auth = ResourceProtector()
require_auth.register_token_validator(validator)
APP = Flask(__name__)
@APP.route("/")
@require_auth(None)
def home():
return "Authorized!"
The test should look something like this:
from my_module import APP
class TestApi:
@property
def client(self):
return APP.test_client()
def test_response_without_oauth(self):
response = self.client.get("/")
assert response.status_code == 401
def test_response_with_auth0(self):
# Do some mocking or some magic
response = self.client.get("/")
assert response.text == "Authorized!"
Solution
The Flask docs subtly refer to Application Factories as having a use case involving testing:
So why would you want to do this?
- Testing. You can have instances of the application with different settings to test every case.
...
We can use an application factory to keep code out of the top-level which makes life hard for mocking (because by the time you've imported the module, the thing you're hoping to mock has already been defined.)
from authlib.integrations.flask_oauth2 import ResourceProtector
def create_app():
"""The name `create_app` is magic: you can't rename it"""
require_auth = ResourceProtector()
require_auth.register_token_validator(validator)
app = Flask(__name__)
@app.route("/")
@require_auth(None)
def home():
return "Authorized!"
This means we can then test the route as normal like this:
from my_module import create_app
from authlib.integrations.flask_oauth2 import ResourceProtector
class TestApi:
def test_endpoint_is_protected(self):
client = create_app().test_client()
with patch("my_module.ResourceProtector", wraps=ResourceProtector) as mock:
client().get(self.ENDPOINT)
mock.assert_called_once()
To then test the route without having to worry about auth at all, you could tweak the server a little bit to use the optional
argument:
def create_app(auth_is_optional = False):
...
@app.route("/")
@require_auth(None, optional=auth_is_optional)
def home():
return "Authorized!"
We can now ignore auth in testing by calling create_app
with auth_is_optional=True
.
This approach, of having keyword arguments to create_app
for config, is recommended by the docs:
Make it possible to pass in configuration values for unit tests so that you don’t have to create config files on the filesystem.
Answered By - LondonRob
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.