user6003691
user6003691

Reputation:

Cathing flask abort status code within a test_request_context

I want to understand how I can catch an HTTPException raised by flask.abort while using a test_request_context in a test for the calling method only.

# example.py
import flask

@api.route('/', methods=['POST'])
def do_stuff():
    param_a = get_param()
    return jsonify(a=param_a)

# check if request is json, return http error codes otherwise
def get_param():
    if flask.request.is_json():
        try:
           data = flask.request.get_json()
           a = data('param_a')
        except(ValueError):
            abort(400)
    else:
        abort(405)


# test_example.py
from app import app  # where app = Flask(__name__)
from example import get_param
import flask

def test_get_param(app):
    with app.test_request_context('/', data=flask.json.dumps(good_json), content_type='application/json'):
        assert a == get_param()

In the get_param method above, I try to abort if the is_json() or the get_json() fail. To test this, I pass test_request_context without a content_type and, based on this blog and this answer, I tried adding a nested context manager like so:

# test_example.py
from app import app  # where app = Flask(__name__)
from example import get_param
from werkzeug.exceptions import HTTPException
import flask

def test_get_param_aborts(app):
    with app.test_request_context('/', data=flask.json.dumps('http://example', 'nope'), content_type=''):
        with pytest.raises(HTTPException) as httperror:
            get_param()
            assert 405 == httperror

but I get a assert 405 == <ExceptionInfo for raises contextmanager> assertion error.

Can someone please explain this and suggest a way to test the abort in this get_param method?

Update: Based on @tmt's answer, I modified the test. However, even though the test passes, while debugging I notice that the two assertions are never reached!

# test_example.py
from app import app  # where app = Flask(__name__)
from example import get_param
from werkzeug.exceptions import HTTPException
import flask

def test_get_param_aborts(app):
    with app.test_request_context('/', data=flask.json.dumps('http://example', 'nope'), content_type=''):
        with pytest.raises(HTTPException) as httperror:
            get_param()  # <-- this line is reached
            assert 405 == httperror.value.code
            assert 1 ==2

Upvotes: 1

Views: 1161

Answers (1)

tmt
tmt

Reputation: 8614

httperror is an instance of ExceptionInfo which is pytest's own class that describes the exception. Once it happens, httperror would also contain value property which would be the instance of the HTTPException itself. If my memory is correct HTTPException contains code property that equals to the HTTP status code so you can use it to do the assertion:

# test_example.py
from app import app
from example import get_param
from werkzeug.exceptions import HTTPException
import flask

def test_get_param_aborts(app):
    with app.test_request_context('/', data=flask.json.dumps(), content_type=''):
        with pytest.raises(HTTPException) as httperror:
            get_param()

        assert 405 == httperror.value.code

Notes:

  • get_param() needs to be called within pytest.raises() context manager.
  • The assertion needs to be done outside of the context manager because once an exception is raised the context ends.
  • I don't know if pytest.raise is your typo or if it really existed in older versions of pytest. AFAIK it should be pytest.raises.

Upvotes: 2

Related Questions