4

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]])

2 Answers 2

5

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
Sign up to request clarification or add additional context in comments.

1 Comment

I, agree this is more elegant. Somehow I missed this when reading the documentation...
2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.