Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pytest monkeypatch.setattr() inside of test class method

Tags:

python

pytest

I have a test class with few test methods and I want to patch some app classes and methods from the test methods. In pytest docs I found an example of how to use monkeypatch module for tests. It that example all tests are just functions, not testclass methods.

But I have a class with test methods:

class MyTest(TestCase):
   def setUp():
     pass

   def test_classmethod(self, monkeypatch):
     # here I want to use monkeypatch.setattr()
     pass

And just passing monkeypatch as method param is obviously doesn't work. So looks like py.test magic doesn't work this way.

So the question is simple and maybe stupid: how can I use monkeypatch.setattr() for pytest inside from the test class method?

like image 207
Paul Avatar asked Feb 19 '17 19:02

Paul


People also ask

What is monkeypatch Setattr?

monkeypatch can be used to patch functions dependent on the user to always return a specific value. In this example, monkeypatch. setattr is used to patch Path. home so that the known testing path Path("/abc") is always used when the test is run. This removes any dependency on the running user for testing purposes.

Is monkeypatch part of Pytest?

This test function utilizes the 'monkeypatch' fixture that is part of pytest, which means that the 'monkeypatch' fixture is passed into the function as an argument. The test function starts by creating a mock version of the getcwd() function (the 'mock_getcwd()' function) which returns a specified value.

Why do we need monkey patching?

Monkey patching is reopening the existing classes or methods in class at runtime and changing the behavior, which should be used cautiously, or you should use it only when you really need to. As Python is a dynamic programming language, Classes are mutable so you can reopen them and modify or even replace them.


2 Answers

I had exactly the same problem. This works perfectly

import unittest
import pandas as pd
from _pytest.monkeypatch import MonkeyPatch
from src.geipan_data import loadLongitudeLatitudeDateTestimony

class TestGeipanData(unittest.TestCase):

    def setUp(self):
        self.monkeypatch = MonkeyPatch()

    def test_loadLongitudeLatitudeDateTestimony(self):

        def read_csv(*args, **kwargs):
            return pd.DataFrame({
                'obs_date_heure': ['2010-05-21', '1926-05-21'],
                'obs_1_lon': [45.123, 78.4564],
                'obs_1_lat': [32.123, 98.4564],
            })

        self.monkeypatch.setattr(pd, 'read_csv', read_csv)

        df = loadLongitudeLatitudeDateTestimony()

        self.assertListEqual(
            df.columns.values.tolist(),
            ['obs_date_heure', 'obs_1_lon', 'obs_1_lat']
        )

In this example I do mock the pd.read_csv method with monkey patch and I uses asserListEqual that extends from unittest.TestCase

like image 197
Artiom Avatar answered Sep 25 '22 02:09

Artiom


It can't work in this form

While pytest supports receiving fixtures via test function arguments for non-unittest test methods, unittest.TestCase methods cannot directly receive fixture function arguments as implementing that is likely to inflict on the ability to run general unittest.TestCase test suites.

You might create monkeypatch directly

from _pytest.monkeypatch import MonkeyPatch

class MyTest(TestCase):
   def setUp():
     self.monkeypatch = MonkeyPatch()

   def test_classmethod(self):
     self.monkeypatch.setattr ...
     ...

or create own fixture, which will add monkeypatch to your class, and use @pytest.mark.usefixtures

@pytest.fixture(scope="class")
def monkeypatch_for_class(request):
    request.cls.monkeypatch = MonkeyPatch()

@pytest.mark.usefixtures("monkeypatch_for_class")
class MyTest(TestCase):
   def setUp():
     pass

   def test_classmethod(self):
     self.monkeypatch.setattr ...
     ...
like image 43
Piotr Dawidiuk Avatar answered Sep 23 '22 02:09

Piotr Dawidiuk