4

I'm trying to test with Flask-Testing a Flask-SQLAlchemy model. More precisely a static method of this model that uses first_or_404() and I cannot find a way to make my test work.

Here a self contained example that highlight the issue:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask.ext.testing import TestCase

db = SQLAlchemy()

class ModelToTest(db.Model):
    __tablename__ = 'model_to_test'
    identifier = db.Column(db.String(80), unique=True, nullable=False, primary_key=True)

    @staticmethod
    def get_by_identifier(identifier):
        return ModelToTest.query.filter_by(identifier=identifier).first_or_404()

class Config:
    DEBUG = True
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

class TestGetByIdentifier(TestCase):

    def create_app(self):
        app = Flask('test')
        app.config.from_object(Config())
        db.init_app(app)
        return app

    def setUp(self):
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

    def test_get_by_identifier(self):
        self.assert404(ModelToTest.get_by_identifier('identifier'))

I got the error:

(my_env) PS C:\Dev\Test\Python\test_flask> nosetests-3.4.exe
E
======================================================================
ERROR: test_get_by_identifier (test_flask.TestGetByIdentifier)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Dev\Test\Python\test_flask\test_flask.py", line 37, in test_get_by_identifier
    self.assert404(ModelToTest.get_by_identifier('identifier'))
  File "C:\Dev\Test\Python\test_flask\test_flask.py", line 13, in get_by_identifier
    return ModelToTest.query.filter_by(identifier=identifier).first_or_404()
  File "c:\\my_env\lib\site-packages\flask_sqlalchemy\__init__.py", line 431, in first_or_404
    abort(404)
  File "c:\\my_env\lib\site-packages\werkzeug\exceptions.py", line 646, in __call__
    raise self.mapping[code](*args, **kwargs)
werkzeug.exceptions.NotFound: 404: Not Found

----------------------------------------------------------------------
Ran 1 test in 0.913s

So the line self.assert404(ModelToTest.get_by_identifier('identifier')) does generate an exception in the first_or_404() call and this exception is a werkzeug.exceptions.NotFound, it does not seems to be what's expected by self.assert404().

Requirements to run this test are:

  • flask
  • flask-sqlalchemy
  • flask-testing

It is worth noting that when I use the function in the application it behaves as expected.

Thanks in advance.

Johan
  • 3,728
  • 16
  • 25

1 Answers1

5

I'm quoting the answer I've received on GitHub:

https://github.com/jarus/flask-testing/issues/89

I believe this is a misunderstanding about the way to drive the test. The first_or_404 function will indeed raise a NotFound exception. When in the context of the request, the exception will bubble up, be handled, and turn into a 404 http response, which is what flask-testing is looking for.

However, you are not in the context of a request in this case since you are calling the method directly and it is simply resulting in an exception. You could do this to make the test work

from werkzeug.exceptions import NotFound

def test_get_by_identifier(self):
    with self.assertRaises(NotFound):
        ModelToTest.get_by_identifier('identifier')

Or, you can stick that function behind a route and test it using self.client which will correctly give you a 404 http response. However, testing the exception (without the use of flask-testing) may be more appropriate in this case given the level you are testing at.

Johan
  • 3,728
  • 16
  • 25