How to use pytest-mock while debugging - python

I think I am missing something fundamental here and could really use some help.
I am writing a unittest using the pytest-mock mocker feature, like this
def my_unittest(mocker):
m = mocker.spy(my_module, 'my_func')
calling_a_func_which_calls_my_func()
# Check it was called
assert m.call_count == 1
Now, how do I call my_unittest while debugging? I.e. how do I get an instance of the mocker object, which I can then pass to the function? Something like
mocker = pytest_mock.mocker()
my_unittest(mocker)
Again, please enlighten me. It really feels like I am missing something here. Thanks.

Related

Programmatically register function as a test function in pytest

I would like to programmatically add or mark a function as a test-case in pytest, so instead of writing
def test_my_function():
pass
I would like to do something like (pseudo-api, I know neither pytest.add_test nor pytest.testcase exist by that identifier).
def a_function_specification():
pass
pytest.add_test(a_function_specification)
or
I would like to do something like
#pytest.testcase
def a_function_specification():
pass
Basically I would like to write some test-case-generating decorator that isn't exactly working like pytest.mark/parametrizing which is why I started to dig into the internals but I haven't found an obvious way how this can be done for python code.
The YAML example in the pytest docs seem to use pytest.Item but I have a hard time mapping this to something that would work within python and not as part of a non-Python-file test collection.
Starting from version 2.6, pytest support:
nose-style __test__ attribute on modules, classes and functions, including unittest-style Classes. If set to False, the test will not be collected.
So, you need to add this attribute.
One approach is:
def not_a_test1():
assert 1 + 2 == 3
not_a_test1.__test__ = True
Another is:
def make_test(func):
func.__test__ = True
return func
#make_test
def not_a_test2():
assert 1 + 2 == 3

Python unit tests: How to patch an entire class and methods

I am trying to write unittests for existing code which is poorly written and I'm finding it very hard to unit test.
def pay(self):
fraud = NewFraudCheck()
result, transaction = fraud.verify_transaction()
the test I have at the moment, I am patching the NewFraudCheck class
#patch checkout.pay.NewFraudCheck
def test_pay(self, mock_fraud_check):
mock_fraud_check.verify_transaction.assert_called()
The test is failing with a ValueError, stating that verify_transaction is not returning enough values to unpack.
I have tried adding
mock_fraud_check.verify_data.return_value = (1, 1231231)
however this doesn't seemt o have any effect.
There are a few issues I'll point out, but the question is missing a few details so hopefully I can address them all in one shot:
Your syntax here is wrong: #patch checkout.pay.NewFraudCheck. It should be #patch('checkout.pay.NewFraudCheck')
There is a missing class somewhere that has the function pay(self) on it. That class lives inside a module somewhere which is important to properly mock NewFraudCheck. I'll refer to that missing module as other.
NewFraudCheck needs to be patched at the point where it's looked up. That means, in the mystery module other where there's a class that has pay(self) defined in it, there's presumably an import of from pay import NewFraudCheck. That is where NewFraudCheck is looked up, so your patch will need to look like this: #patch('checkout.other.NewFraudCheck). More info here: http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch
You need to assign/use the return value of your patch, not access verify_transaction directly off of the mock. For instance, it should read like this: mock_fraud_check.return_value.verify_transaction.return_value = (1, 1231231). Notice the inclusion of return_value.
The final test I came up with looked like this and passed:
#mock.patch('checkout.other.NewFraudCheck')
def test_pay(self, mock_fraud_check):
# This is the class that lives in mystery module, 'checkout.other' and calls pay()
other_class = SomeOtherClass()
mock_fraud_check.return_value.verify_transaction.return_value = (1, 1231231)
other_class.pay()
mock_fraud_check.return_value.verify_transaction.assert_called()

Is there a simple way to mock many static methods in python/Django?

I'm coming from a Ruby/Rspec world where it is very simple to mock and setup tests/expects() statements to test static methods.
I was wondering if there is a simple way to test same things in Python. Please disregard what the code is actually doing (i have changed many names in it) I have the following code:
def create_models(pr_dict):
facility = FacilityCreator.doit(pr_dict)
p = PCreator.doit(facility, pr_dict)
pr = PRCreator.doit(p, p.recent, pr_dict)
models_affected = {'f': f,
'pr': pr,}
return models_affected
I have looked at unittest and unittest.mock but it doesn't seem to be eloquent solution if I have many static methods in my method. In other words, I would be doing a #patch within a #patch, etc. etc.
You may want to take a look at the python unittest module. It has mocking and patching modules

Mock scope goes beyond current test

I am mocking a module... here is my sample code
def test_validate(self):
"""Test Base Retriever Dataframe"""
sampleQuoteClass = self.sampleQuoteClass('ThisQuote')
bRet._getAsOfData = MagicMock(return_value=sampleQuoteClass)
dataAsDataFrame = bVal.validate(metaDataName='MyNewQuote')
self.assertTrue(len(dataAsDataFrame) > 0)
This works OK.
Problem is - bRet._getAsOfData is also mocked for the next tests, which incidentally resides in other test class.
This problem only occurs when all the tests are running together as a part of collection.
Sounds like you might want to patch the object instead of mocking it directly. You may need to adjust my example a bit to fit your code, but try something like this:
from mock import patch
def test_validate(self):
"""Test Base Retriever Dataframe"""
sampleQuoteClass = self.sampleQuoteClass('ThisQuote')
with patch('__main__.bRet') as mock_bRet:
mock_bRet._getAsOfData.return_value = sampleQuoteClass
dataAsDataFrame = bVal.validate(metaDataName='MyNewQuote')
self.assertTrue(len(dataAsDataFrame) > 0)
When you patch the object, the mocking will be undone and the object will "go back to normal" once the with block exits, so the mocked state will not carry over to your other tests. It is also possible to use patch as a decorator, but I have always preferred to use it as a context manager. See the documentation linked above for examples of each usage.
Also, patching can be tricky in my experience, so I would suggest you read this useful bit of documentation on "where to patch" as well.

Testing in python arround memoized functions

I'm building a bunch of tests and discovered that some of them are failing when they are all run together. Looking at why they are breaking I discovered it is because we memoize some of the system calls for the setup.
Because I'm mocking the system calls and not the memoized the function is only computed once and so it leaks that information into the subsequent tests.
What is the elegant way to deal with memoized functions:
Mock those instead of the system calls
Create a parameter on the memoize to not work for tests
Mock the memoize decorator to return the parameters
Find where the data is memoized and clean it at the end of each test
Any other ideas ?
Thanks
Maybe it's too late but I found very easy to use delete_memoized:
# your function
#memoize(timeout=50)
def your_long_function():
# do stuff
# your test
def test_get_folder_content_error_handled(self, flf):
delete_memoized(your_long_function)
your_long_function()
I just used this solution in Django (using django-memoize) and it works like a charm!
Try using MagicMock instead of vanilla Mock.
Say your code is something like:
#memoize.Memoize()
def OAuthClient():
"""Returns an Oauth client."""
return 'blah'
Mocking with Mock like this:
mymodule.OauthPasswordClient = mock.Mock()
Returns an error like this:
TypeError: unsupported operand type(s) for +=: 'int' and 'Mock'
But mocking with MagicMock like this:
mymodule.OauthPasswordClient = mock.MagicMock()
Works just fine.

Categories