2

I use Jinja2 with Webapp2 on a GAE project.

I have a base RequestHandler as describe in webapp2_extras.jinja2:

import webapp2

from webapp2_extras import jinja2


def jinja2_factory(app):
    """Set configuration environment for Jinja."""
    config = {my config...}
    j = jinja2.Jinja2(app, config=config)
    return j


class BaseHandler(webapp2.RequestHandler):

    @webapp2.cached_property
    def jinja2(self):
        # Returns a Jinja2 renderer cached in the app registry.
        return jinja2.get_jinja2(factory=jinja2_factory, app=self.app)

    def render_response(self, _template, **context):
        # Renders a template and writes the result to the response.
        rv = self.jinja2.render_template(_template, **context)
        self.response.write(rv)

And a view handler as:

class MyHandler(BaseHandler):
    def get(self):
        context = {'message': 'Hello, world!'}
        self.render_response('my_template.html', **context)

My templates are in the default location (templates).

The app works well on dev server, and the template is correctly rendered.

But when I try to unittest MyHandler with

import unittest
import webapp2
import webstest

class MyHandlerTest(unittest.TestCase):

    def setUp(self):
        application = webapp2.WSGIApplication([('/', MyHandler)])
        self.testapp = webtest.TestApp(application)

    def test_response(self):
        response = application.get_response('/')
        ...

application.get_response('/my-view') raise an exception: TemplateNotFound: my_template.html.

Is there something I missed? Like a jinja2 environment or template loader configuration?

greg
  • 2,339
  • 1
  • 18
  • 23

1 Answers1

3

Problem origin: Jinja2 default loader searches files in a relative ./templates/ directory. When you run your GAE application on the development server this path is relative to the root of your application. But when you run your unittests this path is relative to your unittest files.

Solution: Not really an ideal solution, but here a trick I did to solve my problem.

I updated the jinja2 factory to add a dynamic template path, set in app config:

def jinja2_factory(app):
    """Set configuration environment for Jinja."""
    config = {'template_path': app.config.get('templates_path', 'templates'),}
    j = jinja2.Jinja2(app, config=config)
    return j

And I set an absolute path to the templates in the setUp of my unittests:

class MyHandlerTest(unittest.TestCase):

    def setUp(self):
        # Set template path for loader
        start = os.path.dirname(__file__)
        rel_path = os.path.join(start, '../../templates') # Path to my template
        abs_path = os.path.realpath(rel_path)
        application.config.update({'templates_path': abs_path})
greg
  • 2,339
  • 1
  • 18
  • 23
  • Thanks! You can avoid having to set the path in configuration by generating an absolute path based on the module/file that contains your factory function. You're already generating an absolute path in that way, it's just that you're doing it in your test setUp function. If you do it in the factory function you won't have to remember to do any configuration and it'll work both live and under test. eg: `config = {'template_path': os.path.join(os.path.dirname(__file__), 'templates/')}` (assuming both the file containing the factory function and your template dir are at the root of the project). – maltem-za Sep 26 '16 at 14:49
  • please help with this for me if you can https://stackoverflow.com/questions/67661017/jinja2-template-not-found-unsolved – yishairasowsky May 23 '21 at 14:57