Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

py.test use monkeypatch in custom funcargs

I use py.test and really like the funcarg approach to inject objects into test functions. In my testing I need to work with Mock objects, as I have a lot external dependencies. I use monkeypatch to replace certain attributes with the mock objects.

The problem I have is, that I will often end up with a bunch of tests that will use a certain funcarg and always require the same attributes patched. So far I replace the attributes in every test function.

Is there a way to use monkeypatch in my funcarg functions, and remove this duplicated code from the individual tests?

import sys
import pytest
from mock import Mock


#----------------------------------------------------------------------
def pytest_funcarg__api(request):
    """"""
    api = myclass()
    #do some initialisation...
    return api


#----------------------------------------------------------------------
def test_bla1(monkeypatch, api):
    """"""
    monkeypatch.setattr(api,"get_external_stuff",Mock())
    monkeypatch.setattr(api,"morestuff",Mock())

    api.do_something1()
    assert not api.a

#----------------------------------------------------------------------
def test_bla2(monkeypatch, api):
    """"""
    monkeypatch.setattr(api,"get_external_stuff",Mock())
    monkeypatch.setattr(api,"morestuff",Mock())

    api.do_something2()
    assert api.b


if __name__=='__main__':
    pytest.main(args=["-v",sys.argv[0]])
like image 403
circus Avatar asked Feb 24 '23 04:02

circus


2 Answers

You can use the documented getfuncargvalue function to internally use a function argument from another function argument's factory:

def pytest_funcarg__api(request):
    api = myclass()
    #do some initialisation...
    mp = request.getfuncargvalue("monkeypatch")
    mp.setattr(api,"get_external_stuff", Mock())
    mp.setattr(api,"morestuff", Mock())
    return api
like image 108
hpk42 Avatar answered Feb 26 '23 22:02

hpk42


This should work:

def pytest_funcarg__api(request):
    """"""
    api = myclass()
    #do some initialisation...
    mp_plugin = request.config.pluginmanager.getplugin("monkeypatch")
    monkeypatch = mp_plugin.pytest_funcarg__monkeypatch(request)
    monkeypatch.setattr(api,"get_external_stuff",Mock())
    monkeypatch.setattr(api,"morestuff",Mock())
    return api

The trick here is two-fold:

  1. We get the monkeypatch plugin using config.pluginmanager.
  2. We fool the monkeypatch plugin into thinking its called by py.test's dependency injection code, by calling its pytest_funcarg__monkeypatch() funcarg-interface with our very own request object.
like image 26
Boaz Yaniv Avatar answered Feb 26 '23 21:02

Boaz Yaniv