writing testable view code

69 views
Skip to first unread message

Jacob Hite

unread,
Aug 24, 2012, 6:09:48 AM8/24/12
to pylons-...@googlegroups.com
Hello all,

A lot of the pyramid docs will have view callable examples similar to this to the toy code I've provided below. (I'm using traversal which means I am given a context object, but using url dispatch is similar).

class UserView(BaseLayout):
    @view_config(context='model.user.User', renderer='template/user.pt', permission='view')
    def user(self):
        return dict(user=self.context)

    @view_config(name='edit', context='model.user.User', renderer='template/edit_user.pt', permission='edit')
    def edit_user_post(self):
        if self.user_form.form.validate():
            self.user_form.form.bind(self.context)

            return HTTPFound(self.request.resource_url(self.context))

        return HTTPNotFound()

I had a lot of code that was almost trivial thanks to use of traversal and form_encode libraries.  Because of this, even though I cringed a bit I never wrote a 'manager' layer of code for a lot of domain entities that would take care of more complex validations beyond just form validation.

The big problem here I've found is that code like the above is very untestable.  I have to test the rendered view to make sure the user object changed correctly when edited (eg: first name changed).  But, while I think important, testing a rendered view for strings etc, is very fragile and breaks every time the designer comes in and changes the template code.

It would be nicer to test a manager layer of code which returns the objects where values can be tested directly (these tests would be in addition to direct tests on the model itself - because we want to test manager layer validation is working correctly). But it seems unfortunate to introduce a whole new layer of code throughout the app just for testability.

Does anyone have any good recommendations on a better approach/design to view - model interaction to increase testability?  And does the Pyramid community have recommended ways to work with designers, templates and test view code?

Thanks!

Robert Forkel

unread,
Aug 24, 2012, 6:37:57 AM8/24/12
to pylons-...@googlegroups.com
Hm. Isn't the point of using libraries like form_encode exactly so
that you don't have to write the tests for this yourself? If
form_encode is supposed to make sure model attributes are changed
according to form input, but you do not want to rely on this, there's
a lot of testing ahead, i guess.
I'm using deform for form handling, so my code comes in the form of
validators and functions that handle validated form input. So I have
to test the validators and the input handlers, and that's it. But
maybe these functions to handle validated form input are already the
"manager" layer you are talking about. I might feel uncomfortable
myself, if the form library acted directly on sqlalchemy mapped
objects.
regards
robert
> --
> You received this message because you are subscribed to the Google Groups
> "pylons-discuss" group.
> To post to this group, send email to pylons-...@googlegroups.com.
> To unsubscribe from this group, send email to
> pylons-discus...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/pylons-discuss?hl=en.

Jason

unread,
Aug 24, 2012, 8:41:18 AM8/24/12
to pylons-...@googlegroups.com


On Friday, August 24, 2012 6:09:48 AM UTC-4, lostdorje wrote:
Hello all,

A lot of the pyramid docs will have view callable examples similar to this to the toy code I've provided below. (I'm using traversal which means I am given a context object, but using url dispatch is similar).

class UserView(BaseLayout):
    @view_config(context='model.user.User', renderer='template/user.pt', permission='view')
    def user(self):
        return dict(user=self.context)

    @view_config(name='edit', context='model.user.User', renderer='template/edit_user.pt', permission='edit')
    def edit_user_post(self):
        if self.user_form.form.validate():
            self.user_form.form.bind(self.context)

            return HTTPFound(self.request.resource_url(self.context))

        return HTTPNotFound()

I had a lot of code that was almost trivial thanks to use of traversal and form_encode libraries.  Because of this, even though I cringed a bit I never wrote a 'manager' layer of code for a lot of domain entities that would take care of more complex validations beyond just form validation.

The big problem here I've found is that code like the above is very untestable.  I have to test the rendered view to make sure the user object changed correctly when edited (eg: first name changed).  But, while I think important, testing a rendered view for strings etc, is very fragile and breaks every time the designer comes in and changes the template code.



You could do unittesting and instead of testing the rendered result of your view-callables, test your view-callables as functions.  Pyramid has some utilities for helping setup dummy requests and unittesting http://pyramid.readthedocs.org/en/1.3-branch/narr/testing.html. Then you can call your view-callables directly and test the resulting dict or HTTPException.

When my view callable changes something in the database I will check that in the unittest by doing a query on the database to ensure it changed (so I am not relying on looking for a changed value in a rendered template). To get this setup I build the database in the setup and I roll it back in the tearDown so none of the changes are kept.

--
Jason 

Jacob Hite

unread,
Aug 24, 2012, 11:38:25 PM8/24/12
to pylons-...@googlegroups.com
Thanks for the responses.  Jason, you pointed me in the right direction...testing the view callables directly rather than after the rendering stage.

Cheers!

--
You received this message because you are subscribed to the Google Groups "pylons-discuss" group.
To view this discussion on the web visit https://groups.google.com/d/msg/pylons-discuss/-/6LELTp64Yy8J.
Reply all
Reply to author
Forward
0 new messages