Generate URLs for Flask test client with url_for function

You can call url_for() in test request context that created with app.test_request_context() method. There are three methods to achieve this.

With setup and teardown

Since you have created the setup and teardown method, just like what I normally do with unittest, you can just push a test request context in setup method then pop it in teardown method:

class TestViews(object):

    @classmethod
    def setup_class(cls):
        cls.app = create_app()
        cls.app.testing = True
        cls.client = cls.app.test_client()
        cls.context = cls.app.test_request_context()  # create the context object
        cls.context.push()  # push the context

    @classmethod
    def teardown_class(cls):
        cls.context.pop()  # pop the context

    def test_create_user(self):
        """
        Tests the creation of a new user.
        """
        view = TestViews.client.get(url_for('create_users')).status_code == 200

With pytest-flask

Besides, you can also just use pytest-flask. With pytest-flask, you can access to context bound objects (url_for, request, session) without context managers:

def test_app(client):
    assert client.get(url_for('myview')).status_code == 200

With autouse fixture

If you don't want to install the plugin, you can just use the following fixtures to do similar things (stolen from the source of pytest-flask):

@pytest.fixture
def app():
    app = create_app('testing')
    return app


@pytest.fixture(autouse=True)
def _push_request_context(request, app):
    ctx = app.test_request_context()  # create context
    ctx.push()  # push

    def teardown():
        ctx.pop()  # pop

    request.addfinalizer(teardown)  # set teardown

Making requests with the test client does indeed push an app context (indirectly). However, you're confusing the fact that url_for is visually inside the test request call with the idea that it is actually called inside. The url_for call is evaluated first, the result is passed to client.get.

url_for is typically for generating URLs within the app, unit tests are external. Typically, you just write exactly the URL you're trying to test in the request instead of generating it.

self.client.get('/users/create')

If you really want to use url_for here, you must do it in an app context. Note that when you're in an app context but not a request context, you must set the SERVER_NAME config and also pass _external=False. But again, you should probably just write out the URL you're trying to test.

app.config['SERVER_NAME'] = 'localhost'

with self.app.app_context():
    url = url_for(..., _external=False)

self.client.get(url, ...)