[pytest-dev] Working with other dependency injection systems
holger krekel
holger at merlinux.eu
Mon Sep 7 10:01:43 CEST 2015
On Thu, Aug 13, 2015 at 17:47 -0400, Kai Groner wrote:
> Hi holger,
>
> Thanks for your response. Sorry I haven't followed up sooner.
same here :)
> On Thu, May 7, 2015 at 7:21 AM, holger krekel <holger at merlinux.eu> wrote:
>
> > Hi Kai,
> >
> > On Thu, Apr 30, 2015 at 19:28 -0400, Kai Groner wrote:
> > > I'm trying to figure out how I can test a code base that uses an existing
> > > dependency injection system. I've run into two problems, and I have
> > > solutions for each of them but I think maybe there is a better way, so
> > I'm
> > > looking for some advice.
> >
> > Could you provide a simple abstract example?
> > Here and also in the following i don't really understand the background.
> >
>
> The DI system we are using is called jeni. I'll try to keep things simple
> here so you don't need to know a lot about it, but here's the url.
> https://github.com/rduplain/jeni-python
>
> This is untested code, that I hope will be illustrative. If you think it
> would be helpful to run it, let me know and I will make sure it works.
>
> We write code sort of like this:
>
> @route('login', method='POST')
> @jeni.annotate
> def login(
> username: 'form:username',
> password: 'form:password',
> user_lookup: 'user_lookup',
> session_init: 'session_init'):
> user = user_lookup(username)
> if user is None:
> raise ValueError
> if not user.check_password(password):
> raise ValueError
> session_init(user)
>
>
> Here is an example of an injector prototyped with a trivial user_lookup
> implementation:
>
> class Injector(jeni.Injector): pass
>
> @Injector.factory
> def user_lookup_factory():
> class User:
> def __init__(self, username, password=None):
> self.username, self.password = username, password
>
> def check_password(self, password):
> return password == self.password
>
> def user_lookup(username):
> return User(username, username)
>
>
> Calling this, looks like:
>
>
> with Injector() as inj:
> inj.apply(login)
>
> There is a partial application mechanism, that creates a wrapper that
> resolves injector bindings at call time.
>
> @jeni.annotate
> def test_login(login: jeni.partial(login)):
>
> login(username='kai', password='kai')
>
> with raises(LookupError):
>
> login(username='kai', password='KAI')
>
>
> with Injector() as inj:
>
> inj.apply(test_login)
>
> Because we need to write a lot of tests, I want to avoid writing the with
> block with every test. I thought maybe I could use py.test hooks to do
> this.
Please checkout https://pytest.org/latest/plugins.html#hookwrapper-executing-around-other-hooks
It should be the right hook to systematically do your with-injector
logic.
> > The first problem is how do I override how a test using this injector is
> > > called? I think I want to use the pytest_runtest_call hook, but it still
> > > tries to invoke the test without the injector. My current solution is to
> > > use the experimental hookwrapper mechanism to replace item.obj with a
> > > partially bound version, then restore it afterward.
> >
>
> Here my problem is that py.test looks for a ``login`` fixture and when it
> finds none it decides the test cannot proceed.
You probably need to provide this "login" fixture even if it's empty
and work is done in the hook above. not sure i get all your semantics
100 % though.
best,
holger
> > > The second problem I'm having is the py.test fixture system is trying to
> > > resolve arguments that are provided by this other injector. How can I
> > tell
> > > it that some of these don't need to be provided? My current solution is
> > to
> > > blind py.test with a wrapper function with a *a, **kw signature. I use
> > > functools.wraps, so annotations are introspectable for the other
> > injector,
> > > and delete the __wrapped__ attribute to prevent py.test from
> > introspecting
> > > it. Is there a nicer, and perhaps less blunt, way to influence the
> > funcarg
> > > fixture behaviors? I've tried a couple things with the
> > > pytest_collection_modifyitems hook, but haven't gotten anything that
> > works
> > > yet.
> > >
> > > Other details to know about this injection system:
> > > - we want to build and teardown the injector with each test
> > > - we may want to configure the injector differently for some tests
> > > (possibly with fixture data from py.test)
>
>
>
> Kai
--
about me: http://holgerkrekel.net/about-me/
contracting: http://merlinux.eu
More information about the pytest-dev
mailing list