Package: python-resources
Why do we need this libraryWe are not satisfied with classical xUnit way of setup and teardown. We prefer
concise approach of py.test over the verbosity of standard unittest.We found ourselves copying and pasting the same boilerplate code from one test
to another or creating extensive structure of test class hierarchy.py.test fixtures, injected in test functions as parameter names, is
different approach for fixture management. It’s neither worse nor better,
but we found it to be not as flexible as we need.Some questions, that we wanted to solve often, looked like:I have a py.test fixture which creates a new user with default set of
properties. Is there a way I can create a user with different name by the same
fixture?Is there a way to create two users in one test case with the same fixture?Is there an easy recipe to create a user first, and then, say, a todo item for
this particular user in another, separate, fixture?Sure enough, we can handle or work around all these issues somehow with xUnit
setups and teardowns or py.test fixtures, but we wanted something more flexible,
easy and convenient to use. That’s why we createdresourceslibrary.How do we use itFirst, we define functions which we call “resource makers”. These makers are
responsible for creating and destroying resources. It’s like setup and teardown
in one callable.fromresourcesimportresources@resources.register_funcdefuser(email='joe@example.com',password='password',username='Joe'):user=User.objects.create(email=email,password=password,username=username)try:yielduserfinally:user.delete()The flow is simple: we create, we yield, we destroy.We get a number of resource makers, and we group them into modules, liketests/resources_core.py,tests/resources_users.py, etc.Then, in a test file, where we plan to use resources, we import the same global
object, load resource modules we need, and activate them in tests.fromresourcesimportresourcesresources.register_mod('tests.resources_core')resources.register_mod('tests.resources_users')deftest_user_properties():withresources.user_ctx()asuser:assertuser.username=='Joe'This is where a little bit of magic happens. Once you define and register the
resource maker with namefoo, a context managerfoo_ctxis created for
your convenience. This context manager creates a new resource instance with the
corresponding maker function, and destroys the object the way you defined, once
the code flow abandons a wrapping “with”-context.When it shinesAt this point and maybe not so exciting. Yeah, everyone can write the code like
this,  the difference is that we actuallydid it:-). We also have a bunch
of nifty features making the whole stuff more interesting.Feature 1. Customizeable resourcesContexts are better than py.test fixtures, because they are customizeable.
Provide everything you need to context manager, and it will be passed to
resource maker function as an arguments.deftest_user_properties():withresources.user_ctx(name='Mary')asuser:assertuser.username=='Mary'Feature 2. Global object scope and dependent resourcesWe need to have access to resources at different stages of our tests: to get
access to object’s properties and methods, to initiate another, dependent
fixture instance, and finally to tear down everything.As soon as you enter the context withresources.foo_ctx()a variableresources.foowill be created and will be available from everywhere,
including your test function, and other resource makers.The latter fact is especially important, because it’s the way we manage
dependent resources. Yet we need some conventions, which resource is created
first, and so on.@resources.register_funcdeftodo_item(content='Foo'):item=TodoItem.objects.create(user=resources.user,content=content)We agreed that we create user resource first, and todo item afterwards, and
created a new resource maker, taking advantage of this convention.We use it like this:deftest_todo_item_properties():withresources.user_ctx(),resources.todo_item_ctx():assertresources.todo_item.content=='Foo'By the way, if you are still stuck with python2.6, several context managers in
the same “with” expression aren’t available for you yet. Usecontextlib.nestedto avoid deep indentation.Feature 3. Several resources of the same class, and tuneable resource namesSometimes we need to create a couple of resources of the same type, instead of
just one instance. It’s not a problem, if you don’t want to use global
namespace to get access to them. Otherwise you must create a unique identifier
for every resource.Actually, it’s trivial. All you should do is provide a special_nameattribute
to context manager constructor. This attribute won’t be passed to your resource
maker function.deftest_a_couple_of_users():withresources.user_ctx(username='Adam',_name='adam'),\resurces.user_ctx(username='Eve',_name='eve'):assertresources.adam.username=='Adam'assertresources.eve.username=='Eve'Feature 4. Function decoratorsContext manager can work as a decorator too. When we use it like this, an extra
argument will be passed to the function.@resources.user_ctx()deftest_user_properties(user):assertuser.username=='Joe'We should say that usually it works, but to make it work along with py.test
which performs deep introspection of function signatures, we made in with some
“dirty hacks” inside, and you may find out that in some cases the chain of
decorators dies with a misleading exception. We’d recommend to use context
managers instead of decorators, wherever possible.Feature 5. Resource managersYes, we do use setup and teardown methods too. If every function in your test
suite uses the same set of resources, it would be counterproductive to write
the same chain of decorators or context managers over and over again.In this case we use another concept: resource managers. Every resource makerfoocreates theresources.foo_mgrinstance, havingstartandstopmethods. Thestartmethod accepts all arguments which
thefoo_ctxfunction does, including special_nameargument.
Thestopmethod has only one optional_nameargument, and is used to
destroy previously created instance.Here is a py.test exampledefsetup_function(func):resources.user_mgr.start(username='Mary')deftest_user_properties():assertresources.user.username=='Mary'defteardown_function(func):resources.user_mgr.stop()Feature 6. Built-in console and debuggerSometimes it’s nice to take a look on what’s going on within test function and
get access at some point to python console or debugger.Usually you probably do something likeimportpdb;pdb.set_trace()Or, if you need to get shell and have IPython installedfromIPythonimportembed;embed()As it happens often, we added to resources two functions, launching either
debugger or python console inside your test function.fromresourcesimportresourcesdeftest_something():resources.pdb()# to launch debuggerresources.shell()# to launch Python REPLIf you install IPython and ipdb (pip install IPython ipdb), you get more
friendly versions of consoles, otherwise resources fall back to built-in python
console and debugger.Launchpy.testwith-sswitch to be able to fall into interactive console.It’s especially cool that resources object is autocomplete-friendly and it
works well in IPythonIn[1]:resources.resources.johnresources.pdbresources.register_modresources.maryresources.register_funcresources.shellIn[1]:resources.maryOut[1]:{'name':'Mary Moe'}In[2]:resources.user_mgr.start()Out[2]:{'name':'John Doe'}In[3]:resources.todoresources.todo_item_ctxresources.todo_item_mgrIn[3]:resources.todo_item_mgr.start()Out[3]:{'text':'Do something','user':{'name':'John Doe'}}In[4]:resources.todoresources.todo_itemresources.todo_item_ctxresources.todo_item_mgrIn[4]:resources.todo_itemOut[4]:{'text':'Do something','user':{'name':'John Doe'}}Feature 7. Globally accessible storage of constantsThis feature is not something unique toresourcesmodule. Pretty much every
object can act this way, but it is handy to have a convention about the
way you store your test-related constants.It may work like this.resources.TEST_DIRECTORY = '/tmp/foo'
resources.DOMAIN_NAME = 'example.com'
resources.SECRET_KEY = 'foobar'And then, in the test file.from resources import resources
resoures.register_mod('<a resource module name here>')

def test_constants():
    assert resources.TEST_DIRECTORY == '/tmp/foo'
    assert resources.DOMAIN_NAME == 'example.com'
    assert resources.SECRET_KEY == 'foobar'ConclusionTheresourceslibrary works for us in py.test environment. We don’t see any
reasons why it shouldn’t work the same way with nose or classic unitttests.
It works for python versions 2.6, 2.7 and 3.3.Please bear in mind that the libraryis not thread safe, as we are happy with
single threaded tests at this time.And after all… Seven extra features to improve your test suites for free! What
are you waiting for? It’s already improved the quailty of our lives inDoist Inc, and we do hope it will do the same for your
projects.