[TIP] new test parametrization / deprecating "yield"

holger krekel holger at merlinux.eu
Fri May 15 01:38:35 PDT 2009


On Fri, May 15, 2009 at 01:57 -0400, Douglas Philips wrote:
> On or about 2009 May 14, at 8:08 AM, holger krekel indited:
>> On Wed, May 13, 2009 at 15:27 -0400, Doug Philips wrote:
>>> I am not clear from that post and the py.test funcargs page
>>> it links to, just where/how many pytest_generate_tests
>>> functions there can be and when in the test-running-timeline
>>> they/it are run.
>>
>> I see.  I've written up a new blog post which hopefully clarifies
>> this and explains how the generate-tests hook can live
>> in a "local" or "global" plugin:
>>
>>    http://tetamap.wordpress.com/2009/05/14/putting-test-hooks
>
> Thanks. I'm still not clear on when the funcarg providers are called.
> Are they called as the modules are being loaded? (that doesn't seem  
> right).
> Are they called "just in time" as the test method itself is about to be 
> called?

OK, let me try to paraphrase the full funcarg-related protocol in detail: 

1. pytest_generate_tests hooks are called when modules
   are being loaded ("during collection").  This is different from 
   the actual setup and calling of a test function. 

2. Just-in-Time before running a function it is setup which includes
   having values for all function arguments: 

   a) The arguments that were directly provided through 
      metafunc.addcall(funcargs=...) will be taken as is.  

   b) Each missing function argument will be looked up by
      discovering a pytest_funcarg__${ARGNAME} function. 
      This provider function receives a request object, see
      http://codespeak.net/py/trunk/test/funcargs.html#request-object

Here is how you can have setup function arguments "just-in-time". 

    def pytest_generate_tests(metafunc):
        metafunc.addcall(param=1)
        metafunc.addcall(param=2)

    def pytest_funcarg__ARGNAME(request):
        return ExpensiveSetup(request.param)

We generate two test function calls.  The ARGNAME provider 
will be called twice, with the parameter 1 and 2 respectively. 

Briefly speaking, generate_tests passes a "param" when adding
a call that a "just-in-time" funcarg provider can see on its request
and use to parametrize expensive setup.

You can of course cache the setup of function arguments
and finalize/teardown e.g. on test process teardown. 
This means that you do not need any setup/teardown per
method, class etc. and can implement all setup/teardown
logic in exactly one place. 

Makes sense?  
If so i'd add something like the above to the docs or
to another blog post. 

cheers,
holger

> --
>
> This section also caught my eye:
> multiple pytest_generate_hook implementations
> ...
> So, you say, what about a test function with multiple arguments ? could 
> each function argument come from a different generating provider?
>
> This would mean that multiple generators act independently but want to  
> collaborate and combine their values for a given test functions. Well,  
> if you encounter a real need for it, please come forward and we?ll think 
> up a fitting API extension. A couple of days ago i had a "combining 
> funcargs" API call implemented but decided to remove it because i try 
> hard these days to only add features that have a proven need.
> ...
>
> I had thought combining values would be a natural extension.
> For example, one of the things that -some- of our device tests needs is a 
> power controller.
> But not all test code needs it.
> Right now our common TestCase base class arranges for setUp to stash the 
> power controller onto 'self' (during setUp). So that any test which wants 
> a power controller will have it. IMHO it would be a lot cleaner if the 
> test method simply declared it funcargs style:
>
> def test_rapid_fire_power_cycling(self, power_controller, ...):
>     ...
>     while <...>:
>         power_controller.off()
>         sleep(self.power_off_delay
> 	power_controller.on()
>
> the power_controller func_arg being an independent entity for other  
> funcarg supplied parameters.
>
> But again I'm not doing unit testing, I'm just using the unittest  
> framework to do device testing. :)
>
> --Doug
>

-- 
Metaprogramming, Python, Testing: http://tetamap.wordpress.com
Python, PyPy, pytest contracting: http://merlinux.eu 



More information about the testing-in-python mailing list