Hleb
Hleb

Reputation: 7371

How to test Flask router methods with HTTPBasicAuth annotations

I'm new to Python and I try to implement REST API service on Flask. I faced with issue related to testing of my code. My Flask app looks something like that:

from flask import Flask, jsonify, make_response, request
from flask_httpauth import HTTPBasicAuth
import os

auth = HTTPBasicAuth()

@auth.get_password
def get_password(username):
    if username == os.environ['SERVICE_KEY']:
        return os.environ['SERVICE_PASS']
    return None

@auth.error_handler
def unauthorized():
    return make_response(jsonify({'error': 'Unauthorized access'}), 403)

app = Flask(__name__)

tweets = [
    {
        'id': 1,
        'profileId': '1',
        'message': 'My test tweet'
    },
    {
        'id': 2,
        'profileId': '1',
        'message': 'Second tweet!'
    }
]

@app.route('/api/v1/tweets', methods=['GET'])
@auth.login_required
def get_tweets():
    return jsonify({'tweets': tweets}), 200

@app.errorhandler(404)
@auth.login_required
def not_found(error):
    return make_response(jsonify({'error': 'Not found'}), 404)

if __name__ == '__main__':
    app.run(debug=True)

And here is my test (currently it is only for not_found method):

import unittest
from app import app

class TestApp(unittest.TestCase):

    def setUp(self):
        self.app = app.test_client()

    def test_404(self):
        rv = self.app.get('/i-am-not-found')
        self.assertEqual(rv.status_code, 404)


if __name__ == '__main__':
    unittest.main()

But when I try to run test, it fails due to I get 'Unauthorized access' response:

>python test.py
F
======================================================================
FAIL: test_404 (__main__.TestApp)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 25, in test_404
    self.assertEqual(rv.status_code, 404)
AssertionError: 403 != 404

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

Which approach for testing route-methods are more correct to handle authorization? And how can I fix that failed test?

Upvotes: 0

Views: 920

Answers (1)

Tim Thompson
Tim Thompson

Reputation: 311

You need to create a custom header that includes your auth details and send it along with your request. Something like this:

from base64 import b64encode    
...
headers = {'Authorization': 'Basic ' + b64encode("{0}:{1}".format(username, password))}
rv = self.app.get('/i-am-not-found', headers=headers)
...


import unittest
from app import app

class TestApp(unittest.TestCase):

    def setUp(self):
        self.app = app.test_client()

    def test_404(self):
        headers = {
            'Authorization': 'Basic ' + b64encode("username:password")
        }
        rv = self.app.get('/i-am-not-found', headers=headers)
        self.assertEqual(rv.status_code, 404)


if __name__ == '__main__':
    unittest.main()

Your username and password is sent in the form username:password but is base64 encoded. If expanding this there are ways to make this simpler such as extracting into a function to always pass the header and externalising username/password for testing.

EDIT: Additionally I think you should be returning a 401 code here. 401 is usually used when credentials are incorrect, 403 is usually used when you have successfully authenticated yourself but do not have access to a resource. A very simplified example being logged into Facebook but being restricted from accessing another person's photo that is marked as private.

Upvotes: 1

Related Questions