Search code examples
flaskpytest

Testing Flask captured_templates says list isn't callable


I am trying to copy and paste the code from Flask's signals testing with pytest, but it's not working in my app. Everything is the same except for @pytest.fixture(scope="session") in the captured_templates() decorator instead of @contextmanager.

Here's my conftest.py:

import pytest
from flask import template_rendered
from flask_login import FlaskLoginClient

from myapp import create_app

TEST_DATABASE_URI = 'my-db-path'


@pytest.fixture(scope="session")
def app(request):
    """Session-wide test `Flask` application."""
    app = create_app(test_config=True)
    app.test_client_class = FlaskLoginClient

    # Establish an application context before running the tests.
    ctx = app.app_context()
    ctx.push()

    def teardown():
        ctx.pop()

    request.addfinalizer(teardown)
    yield app


@pytest.fixture
def client(app):
    with app.test_client() as client:
        yield client


@pytest.fixture(scope="session")
# copied from the signals link
def captured_templates(app):
    recorded = []

    def record(sender, template, context, **extra):
        recorded.append((template, context))

    template_rendered.connect(record, app)
    try:
        yield recorded
    finally:
        template_rendered.disconnect(record, app)

And here's my test in test_home.py

# copied from the signals link
def test_home(app, captured_templates):
    with captured_templates(app) as templates:
        rv = app.test_client().get('/')
        assert rv.status_code == 200
        assert len(templates) == 1
        # took the rest of the lines from the link out because I just wanted to test the templates existing

I'm using Blueprints for my home route:

home.py

from flask import Blueprint
from flask import render_template

homeBP = Blueprint('home', __name__)


@homeBP.route('/')
@homeBP.route('/home')
def home():
    """Renders the home template"""
    return render_template('website/index.html', title='Home')

And for good measure... index.html

{% extends "layout/layout_form.html" %}
{% block content %}

    <div class="row" style="width:90%">
        Hello world!
    </div>

{% endblock content %}

I'm getting this error:

web_app\test_harness\tests\test_home.py:35 (test_home)
app = <Flask 'myapp'>, captured_templates = []

    def test_home(app, captured_templates):
>       with captured_templates(app) as templates:
E       TypeError: 'list' object is not callable

web_app\test_harness\tests\test_home.py:37: TypeError

How can I fix this so I can test the templates?


Solution

  • I don't know why, but the previous answer to my question is gone, and it had the solution in it?

    The solution:

    1. Replace @pytest.fixture with @contextmanager decorator for captured_templates() (importing contextmanager from contextlib
    2. Don't use captured_templates as a fixture in test_home(), but rather import captured_templates from tests.conftest

    In other words, follow the link more closely and don't try to replace it using pytest.

    conftest.py

    @contextmanager
    def captured_templates(app):
        recorded = []
    
        def record(sender, template, context, **extra):
            recorded.append((template, context))
    
        template_rendered.connect(record, app)
        try:
            yield recorded
        finally:
            template_rendered.disconnect(record, app)
    

    test_home.py

    from tests.conftest import captured_templates
    
    def test_home(app):
        with captured_templates(app) as templates:
            rv = app.test_client().get('/')
            assert rv.status_code == 200
            assert len(templates) == 1