See the 'Testing with the session object' thread for examples on
how to deal with that.
Wichert.
--
Wichert Akkerman <wic...@wiggy.net> It is simple to make things.
http://www.wiggy.net/ It is hard to make things simple.
You removed all context from your reply so I'm not sure what exactly yo
uare replying to.
I suspect you are replying to my reference to the session thread. That
has example code for a TestCase class you can use for your tests that
sets up everything needed to use 'magic' variables like g. It works
just fine for helper methods or anything else.
If you really want to go this route you can either mock it (using the
same pattern as has been suggested to mock pylons.session) or test the
whole stack so PylonsApp takes care of setting up and tearing down the
app context on every request.
app = paste.deploy.loadapp('config:mytestconfig.ini')
app = webtest.TestApp(app)
resp = app.get('/some/test/method')
assert resp.some_test_var == 'foo'
the controller action that responds to the above url can place variables
in environ['paste.testing_variables'] and they'd be bound to the test
response that TestApp.{get,post,put,etc...} returns:
def some_controller_method(self):*
if* *'paste.testing_variables'* *in* request.environ:
request.environ[*'paste.testing_variables'*][*'some_test_var'*]
= 'foo'
However, the "right" way to do it, IMHO, would be to separate the
functions/classes that depend on g in two. For example, say you needed a
pylons.g.db_connection inside a helper function "bar":
def side_effect_free_bar(db_connection):
# do something with db_connection and refrain from accessing global
state
# this should be pretty easy to unit-test since it's output depends
solely on it's arguments.
# You can easily pass a db_connection you've explicitly prepared for
testing
def bar():
# This one you would test inside functional/integration tests, if at
all.
return side_effect_free_bar(pylons.g.db_connection)
HTH,
Alberto
What's wrong with using paste.loadapp to properly set 'g' for every
test that needs it? That way your test environment is the closest to
the production environment -- which is the purpose of having tests in
the first place.
--
Mike Orr <slugg...@gmail.com>
Unit tests want as little as possible of the full environment. And you
want to be able to unit test code that uses things like g.
Well, unit tests (as opposed to functional tests) should test the
target routine as closely as possible, without the interference of
something like 'g' which should be tested separately. After all, a
value on 'g' is the same as the value standalone.
--
Mike Orr <slugg...@gmail.com>
You might want to unittest a method that uses g in some way. More
typically you may want to unit test something that stuffs data in c.
True. I'm just saying if you can code to avoid 'g' and 'c' in your
lib routines, so much the better. I have never found a use for 'g',
except in one case to put a structure of static data that's parsed on
app startup (or read from a cache pickle).
--
Mike Orr <slugg...@gmail.com>
You're not initializing the application completely and you're
wondering why it isn't working? Don't you see the contradiction
there?
> i've also noticed that
> for every test it does a full app initialization - which means ( in my
> case ) querying the database for 50 tables worth of data.
Unit tests always do that: setUp creates a full test environment,
which is discarded after each test. Generally that's considered a
good thing even if it's slow. Python apps have been testing this way
since long before Pylons was created. If you have to read 50 tables
worth of data, it's more a case that you have an unusual application
structure than that Python's testing is screwed up. You may have to
test this application by pre-reading the data at module level and
putting it in place. (And if it's place is in pylons.g, maybe have
some lightweight g-initialization routine that copies the variables
from their test module locations. There's a module_setup function
that may persist between tests, although I'm not sure.
--
Mike Orr <slugg...@gmail.com>
Can we please make sure our test terminology is correct? A unit test
will never do that since it tests as little code as possible in an
isolated environment, preferably using mock objects.
Integration tests however do setup the whole application.
That's my point. Unit tests should not be testing things that use
'g'. Low-level routines should not use 'g'. Only at the higher
integration layer (e.g., controllers) should 'g' get involved. That
way the low-level routines are decoupled from the rest of Pylons, so
they can be used and tested separately. That's the same reason I
added model.init_model() to the SQLAlchemy template, so that the model
could be used without depending on the rest of Pylons.
There's some differences of opinion on where
request/response/g/c/session/cache should properly be used, but by
limiting them to the controller and template as I usually do, it
eliminates unnecessary dependencies between the low-level routines and
the full Pylons environment. This makes it easer to test the routine
code itself without interference from the environment. Thus "def
a(important_value):" instead of "def a(): important_value =
g.important_value".
Obviously there will be some applications where this doesn't work,
where you need something available globally and 'g' is the only
reasonable place to put it. In that case those tests will need the
full environment loaded. Either that or you completely restructure
Pylons to eliminate 'g' and replace it with something else that's more
test-friendly. Or maybe there's a way to build a test structure that
loads a Pylons environment once and uses it for several tests.
However, the reason the environment is normally recreated for every
test is to prevent changes made by one test from leaking into another
test, which invalidates the test.
Putting database lookup values into 'g' was an idea I had not thought
of. When I've come across that situation, I put a function in the
model that returned or updated a cache dict (module global). It is
assumed this function will be valid only if init_model() has been
called. Of course it will fail with a predictable "no database engine
available" error if this is not the case. Now, this structure is safe
if the data does not vary across application instances coexisisting in
the same process, and is ideal if it's read-only. If the data does
vary across application instances, you'll have to put it on 'g'
because that's what 'g' is for -- as a safe place to put
application-instance-specific data.
--
Mike Orr <slugg...@gmail.com>
If you use nose and *don't* use the unittest API, then you can have
module-level setup functions. Create a directory named test with a
file called test_foo. All your tests are normal functions like
test_bar(). You do setup and tear down in functions called
setup_module and teardown_module. It's an easy API to remember ;)
-jj
It's not a misfortune, it is a bug. g is *contextual* to an app's
*instance* so there's no way you can expect to work properly at the module
level since you have little control over what g will be really pointing to
when the module is imported. The fix is as easy as:
def get_Form_Register():
class Form_Register(....):
... g.us_state_keys()
return Form_Register
And then call get_Form_Register() inside the controller's actions where
you know for sure g will be properly initialized.
Better still, don't use g for this. Use a cached query:
# Inside some module
@beaker_cache(...)
def us_states():
return model.States.query(model.States.c.country_code == 'us')
A rule of thumb I try to follow that has save me much troubles (after
causing me many more) is to make module imports as side-effect free as
possible.
(....)
>> Putting database lookup values into 'g' was an idea I had not thought
>> of. When I've come across that situation, I put a function in the
>> model that returned or updated a cache dict (module global). It is
>> assumed this function will be valid only if init_model() has been
>> called. Of course it will fail with a predictable "no database engine
>> available" error if this is not the case. Now, this structure is safe
>> if the data does not vary across application instances coexisisting in
>> the same process, and is ideal if it's read-only. If the data does
>> vary across application instances, you'll have to put it on 'g'
>> because that's what 'g' is for -- as a safe place to put
>> application-instance-specific data.
>
> That could work - but at the expense of needless code.
It's not needless if it is needed for the thing to work properly ;)
> I need these constants when:
> - displaying a form
> - validating a form
> - validating API or other values
> - printing information to the end user
>
> doing a model call every time would work - but at the expense of
> repetitive queries. for all intents , this data are static and a
> dict.
pylons.cache. It will even make your app scale more easily since you can
transparently switch to memcached once your app grows over more than one
machine and if, for any reason, the data your storing turns out to be less
static than you thought (eg: the admin interface is extended to allow to
edit the list of available languages)
Alberto
I should point out that getting non-request data into the validators
is a general weakness in Pylons, one that we haven't found a
definitive answer for. So far I've seen these strategies:
- Pass the data in through the 'state' argument. That's what the
argument is for, yet it's impossible to do with @validate because the
wrapper is run before the action method has started, so there's no
opportunity to pass request-specific data such as the current database
record.
- You can inline @validate code into the action method so that it can
call the validator properly with 'state'. However, the current
structure of the @validate code makes it difficult to port because so
much of it has to do with contingencies in the arguments, making it
difficult to tell which code needs to be copied to the action. Or you
can try to piece together code based on the FormEncode manual, but
that's not Pylons-specific so there's the danger of missing an
important 'if' somewhere. There's a ticket to split @validate into
three parts which can be called individually by actions, and make
@validate a simple wrapper to the parts. I'm hoping this or something
similar will get into Pylons 0.9.8 (the second-next version).
http://pylonshq.com/project/pylonshq/ticket/405
- You can allow the validator to access the model directly and look up
database records. This is a possible violation of MVC and adds a
dependency from the validator to the model, but if the validator is
considered a "controller thing" or a "model thing" then it's arguably
legit. This can be combined with a cache accessor/updater in the
model as both I and Alberto have described, to minimize the number of
database queries for infrequently-changing lookup data.
- But how do you get the current record ID in the URL? The action
knows it, but the validator doesn't if you're using @validate. One
way is to look up the ID in 'c', using a little-known (and I think
undocumented) feature of Pylons that copies all routing args to 'c'
variables.
- But what if the ID is non-numeric, points to a nonexistent record,
or the user does not have permission to view the record? Oh dear,
this is all processing the action does. Does the validator have to
duplicate all this processing? Currently it does if you're using
@validate and letting the validator access the model directly. You
could inline the validation call into the action, but that gets into
our second problem above.
- And now we've seen that 'g' can also be used as a rendezvous point
for validators to find their control data. The pros and cons of this
have already been discussed in this thread so I won't repeat them.
But I do want to thank Jonathan for describing this strategy, because
as I said it's not one I'd thought of. Are you the same Jonathan who
posted the validate parts patch?
- There may be better strategies we haven't discovered yet.
--
Mike Orr <slugg...@gmail.com>