I have a Test class with as many as 50 different method. I want to patch every method with a mock function.
prod = {"foo": "bar"}
def TestClass:
#patch(db.get_product, return_value=prod)
def test_1:
pass
#patch(db.get_product, return_value=prod)
def test_2:
pass
.
.
.
#patch(db.get_product, return_value=prod)
def test_50:
pass
Is there any easy way to do this instead of repeating #patch(db.get_product, return=prod) 50 times?
You can use patch as a class decorator instead:
#patch(db.get_product, return_value=prod)
class TestClass:
def test_1:
pass
def test_2:
pass
.
.
.
def test_50:
pass
Excerpt from the documentation:
Patch can be used as a TestCase class decorator. It works by
decorating each test method in the class. This reduces the boilerplate
code when your test methods share a common patchings set.
Related
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?
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 ...
...
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
I am trying to patch a class that is instantiated by the class I am trying to test, but it doesn't work. I have read the various docs but still haven't found what I am doing wrong. Here is the code snippet:
In tests/Test.py:
from module.ClassToTest import ClassToTest
class Test(object):
#mock.patch('module.ClassToPatch.ClassToPatch', autospec = False)
def setUp(self, my_class_mock):
self.instance = my_class_mock.return_value
self.instance.my_method.return_value = "def"
self.class_to_test = ClassToTest()
def test(self):
val = self.class_to_test.instance.my_method() #Returns 'abc' instead of 'def'
self.assertEqual(val, 'def')
In module/ClassToPatch.py:
class ClassToPatch(object):
def __init__(self):
pass
def my_method(self):
return "abc"
In module/ClassToTest.py:
from module.ClassToPatch import ClassToPatch
class ClassToTest(object):
def __init__:
# Still instantiates the concrete class instead of the mock
self.instance = ClassToPatch()
I know in this case I could easily inject the dependency, but this is just an example. Also, we use a single class per file policy, with the file named like the class, hence the weird import naming.
As norbert mentions, the fix is to change the mock line from
#mock.patch('module.ClassToPatch.ClassToPatch', autospec = False)
to
#mock.patch('module.ClassToTest.ClassToPatch', autospec = False)
According to the docs:
The patch() decorator / context manager makes it easy to mock classes or objects in a module under test. The object you specify will be replaced with a mock (or other object) during the test and restored when the test ends.
You are testing the ClassToTest module, not the ClassToPatch module.
WORKAROUND:
I was unable to patch the decorator and still have access to the wrapped function. but the workaround for me to test the function wrapped in a decorator was the following:
def un_some_method(self):
...
some_method = some_var.some_decorator('somestring')(un_some_method)
This still decorates my function, but gives me access to the function if I wish to test it, reuse it elsewhere...
This is the problem:
I have a module, which has a class in it, and variables that instantiate a class which expose a decorator.
Then within the class inside my module, I use this variable with the instance of my class and decorate a method in my class... To be clear, lets look at some code:
some_var = ClassX()
class SomeClass(object):
#some_var.some_decorator('somestring')
def some_method(self):
...
in my test I want to test the code within some_method and I don't care about the decorator... although my attempts to mock the decorator out was useless... below is some things I tried:
#patch('path_to_classx.ClassX.some_decorator')
#patch('path_to_someclassmodule.some_var')
#patch('path_to_someclassmodule.ClassX')
none of the attempts above prevented the function from being decorated... any idea why?
One option is to patch some_var after you have instantiated it, replacing some_decorator with an identity function so that the decorated method remains unchanged.
import mock
class ClassX(object):
def some_decorator(self, f):
def g(*args, **kwargs):
print("Hello")
return f(*args, **kwargs)
return g
some_var = ClassX()
with mock.patch.object(some_var, 'some_decorator', side_effect=lambda f: f):
class SomeClass(object):
#some_var.some_decorator
def some_method(self):
print "World"
SomeClass().some_method()
I want to mock a function which is called within a class method while testing the class method in a Django project. Consider the following structure:
app/utils.py
def func():
...
return resp # outcome is a HTTPResponse object
app/models.py
from app.utils import func
class MyModel(models.Model):
# fields
def call_func(self):
...
func()
...
app/tests/test_my_model.py
from django.test import TestCase
import mock
from app.models import MyModel
class MyModelTestCase(TestCase):
fixtures = ['my_model_fixtures.json']
def setUp(self):
my_model = MyModel.objects.get(id=1)
#mock.patch('app.utils.func')
def fake_func(self):
return mock.MagicMock(headers={'content-type': 'text/html'},
status_code=2000,
content="Fake 200 Response"))
def test_my_model(self):
my_model.call_func()
... # and asserting the parameters returned by func
When I run the test the mock function fake_func() is avoided and the real func() is called instead. I guess the scope in the mock.patch decorator might be wrong, but I couldn't find a way to make it work. What should I do?
There are three problems with your code:
1) As Daniel Roseman mentioned, you need to patch the module where the function is called, not where it is defined.
2) In addition, you need to decorate the test method that will actually be executing the code that calls the mocked function.
3) Finally, you also need to pass the mocked version in as a parameter to your test method, probably something like this:
fake_response = mock.MagicMock(headers={'content-type': 'text/html'},
status_code=2000,
content="Fake 200 Response"))
class MyModelTestCase(TestCase):
fixtures = ['my_model_fixtures.json']
def setUp(self):
my_model = MyModel.objects.get(id=1)
#mock.patch('app.models.func', return_value=fake_response)
def test_my_model(self, fake_response): # the mock goes in as a param or else you get number of arguments error!
my_model.call_func()
self.assertTrue(fake_response.called)
As the docs explain, you need to mock func in the place it is called, not where it is defined. So:
#mock.patch('app.models.func')
I have tests like this:
import unittest
class TestBase(unittest.TestCase):
def setUp(self):
self.decorator = None
def testA(self):
data = someGeneratorA().generate()
self.assertTrue(self.decorator.someAction(data))
def testB(self):
data = someGeneratorB().generate()
self.assertTrue(self.decorator.someAction(data))
def testC(self):
data = someGeneratorC().generate()
self.assertTrue(self.decorator.someAction(data))
class TestCaseA(TestBase):
def setUp(self):
self.decorator = SomeDecoratorA
class TestCaseB(TestBase):
def setUp(self):
self.decorator = SomeDecoratorB
As you see, TestCaseA and TestCaseB is very similar, so I made TestBase class which implement body of testA, testB and testC method.
TestCaseA different from TestCaseB only decorator parameter.
So, I would like to ask, is any better way to organize my tests? And I have problem beacuse TestBase class - it's test's - shouldn't be runned ever (self.decorator is None so it will rase an exception)
Anything that inherits from unittest.TestCase is seen as a set of tests.
You could instead have your base class not inherit from TestCase, moving that base class to your concrete test classes instead:
class TestBase(object):
# base tests to be reused
class TestCaseA(TestBase, unittest.TestCase):
# Concrete tests, reusing tests defined on TestBase
class TestCaseB(TestBase, unittest.TestCase):
# Concrete tests, reusing tests defined on TestBase