Python magic mock not setting the mocked value - python

I am writing python tests for the first time. I am trying to test a basic mock. I want to return some value that I want when I call the function, rather than a mock object.
Here is the code:
In views:
def myfunction():
return "Actual data"
In test:
class TestBasic(unittest.TestCase):
#patch('trailblazer.views.myfunction')
def testMyFunction(self, val):
print(val)
val.return_value = "Test value"
print(val)
op = myfunction()
print(op)
output:
<MagicMock name='myfunction' id='4520521120'>
<MagicMock name='myfunction' id='4520521120'>
Actual data
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
PS: I don't have my methods in a class and I don't want to change that.

You have a direct reference to myfunction() in your test module, and that reference is never patched. You only patched the reference in the trailblazer.views module.
Your test would work if you used that reference instead of myfunction:
from trailblazer import views
class TestBasic(unittest.TestCase):
#patch('trailblazer.views.myfunction')
def testMyFunction(self, val):
print(val)
val.return_value = "Test value"
print(val)
op = views.myfunction()
print(op)
However, a more meaningful test is to test the code that uses myfunction(). You use mocking to be able to focus on the behaviour of a specific unit of code, where mocking lets you precisely control interactions with other units.
In other words, if you have code like:
def some_function_to_test():
# other things
result = myfunction()
# more things working on result
return final_result
then when testing some_function_to_test() it makes sense to patch myfunction().
I recommend you read up on how Python names work; I highly recommend Facts and myths about Python names and values, together with Where to patch in the unittest.mock documentation.

Related

How to unit test a function when it is calling a member function of one of the argument

I want to unit test the following function in Python:
def get_params(env, secret_fetcher):
try:
url = env['API_URL']
except KeyError:
raise
try:
key = secret_fetcher.get_secret('CLIENT-KEY')
secret = secret_fetcher.get_secret('CLIENT-SECRET')
except:
raise
return url, key, secret
It read one parameter from environment while retrieve the other two from a key vault using an object of KeyVault class secret_fetcher. I call it in my main function like below:
secret_fetcher = SecretFetcher(vault_url)
url, key, secret = get_params(os.environ, secret_fetcher)
I am writing a unit test for this function. For env I am using a dictionary inside the test. However what do I do with second argument of the function whose member function is being called inside the function to test?
class TestApp():
def test_get_params(self):
env = {'WrongField': 'http://test.com/123'}
<mock secret_fetcher>
self.assertRaises(KeyError, get_params, env, secret)
Do I mock secret_fetcher or secret_fetcher.get_secret? Especially when get_secret returns different value when fed with different argument of its own. Should I mock the class SecretFetcher and implement a function get_secret that returns expected output for argument with these two different values?
If you are only intending to test the exception as-is, mocking the secret_fetcher argument is basically inconsequential at this stage, as a simple None value will do as it will never be touched, but here's an example to kick things off:
# include the `get_param` function by import or inline here
import unittest
from unittest.mock import Mock
class TestApp(unittest.TestCase):
def test_get_params_missing_url(self):
env = {'missing': 'fail'}
secret_fetcher = Mock()
with self.assertRaises(KeyError):
get_params(env, secret_fetcher)
(Do note that I prefer using assertRaises as a context manager to ensure a more natural way of writing the calling of a function; do note that the first exception in the with block will prevent subsequent code from being executed in that block, so it's recommended that only one logical expression be in the assertRaises context manager, or at the very least be the last line; i.e. this can only test one exception at a time)
Running this one test:
$ python -m unittest demo.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
However, given that the theory behind using mocks in the context of unit testing is to enable the testing of the code using the bare minimum external dependencies (i.e. using no other real modules, methods or classes; this keep the testing done against just the relevant unit), using the other features provided by unittest.mock.Mock and friends may simplify this goal.
You may wish to ensure that get_secret was called with the correct argument and the expected result was to be returned, should the correct env was provided. Also testing that the error handling was dealt with as expected. Additional methods that may be appended to the TestApp class above:
def test_get_params_success(self):
env = {'API_URL': 'https://api.example.com'}
def get_secret(arg):
return arg
secret_fetcher = Mock()
secret_fetcher.get_secret.side_effect = get_secret
url, key, secret = get_params(env, secret_fetcher)
self.assertEqual(url, 'https://api.example.com')
self.assertEqual(key, 'CLIENT-KEY')
self.assertEqual(secret, 'CLIENT-SECRET')
# Test that the secret_fetcher.get_secret helper was called
# with both arguments
secret_fetcher.get_secret.assert_any_call('CLIENT-KEY')
secret_fetcher.get_secret.assert_any_call('CLIENT-SECRET')
self.assertEqual(
secret_fetcher.get_secret.call_args[0], ('CLIENT-SECRET',))
def test_get_params_failure(self):
env = {'API_URL': 'https://api.example.com'}
secret_fetcher = Mock()
secret_fetcher.get_secret.side_effect = ValueError('wrong value')
with self.assertRaises(ValueError):
get_params(env, secret_fetcher)
# Test secret_fetcher.get_secret helper was only called with
# the first CLIENT-KEY argument
# Python 3.8 can check secret_fetcher.get_secret.call_args.args
self.assertEqual(
secret_fetcher.get_secret.call_args[0], ('CLIENT-KEY',))
Testing it out:
$ python -m unittest demo.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
Note that while I have absolute zero information about what your SecretFetcher class does or has, the three test cases together tested the provided get_params function to ensure that it behaves as expected, including testing how it should have used the secret_fetcher.get_secret, that it handles the error as expected, and that with all the provided test cases tested every line of code in the get_params example provided in the question.
Hopefully this served as a comprehensive example on how mocks might be used to satisfy the goals of unit testing.

Will unittest work for functional (non-object-oriented) program in Python?

I'm a beginner programmer, I've been stuck for the past week trying to write unit tests. I read through the unit test docs and watched two long tutorials on implementing unit testing with Mock. The docs refer to mocking classes extensively, but for functions, I'm not sure if I should be using #patch/with patch, patch.dict{}, side_effect, or some other option to mock a function, specifically the argument to a function.
mymodule.py
def regex():
'''Runs a regex, creates a dict 'data' and then calls scraper(data)'''
def scraper(data):
'''scrapes a website and then calls a function which submits data to a db'''
I would like to create a test that passes in test data to the function scraper . Thank you in advance.
Yes, you can also do unit test using mock for non-object_oriented code.
See example below:
from unittest.mock import MagicMock
def a():
return 10
def b():
print(a())
b()
a = MagicMock(return_value=3)
b()
And the output is:
10
3
In the previous example mock is used to fake/mock the function a(), so you can test function b() in isolation, b() is your SUT, without calling a() real implementation. This can be useful for more complex code, specially when function a() relies on data that might not be available in the unit test level.
You haven't given enough examples to really help, but a couple of observations:
Generally, it's better to structure your code to avoid the need for mocking in tests; to the extent possible, each function should be a self-contained piece of code, which can be called separately.
There's no need to mock arguments; simply pass the test value in.
I'm not sure what's intended with this code:
bar = {'key': 'value'}
def foo(bar):
pass
The bar defined at the outer level is a completely separate variable to the bar used as an argument in the function definition. It is confusing to give them the same name...
A function can be mocked using patch or patch.object with the return_value=... option; often, though, it suggests that the code needs to be refactored to reduce the dependency between the two functions.

Pytest fixtures scoping

Sorry for this question. It may be repetetive but did not find enough on google related to this. I know fixtures are having 4 scopes (function, module, class, session). Just want to know whether these scopes are valid only if I use autouse true?. For autouse=False, can i use any fixture having any scope in a test function
Like I am having a conftest.py file which is having fixture as
#pytest.fixture(scope="module")
def test_fixture():
DO something
print "Module level fixture am called"
test.py file is having below data
class ABC:
def test(test_fixture):
print "Doing something"
def test_2(test_fixture):
print "Executing second test"
Now If I change scope of fixture to session, it will still run and produce the same result.
Does that mean scoping only valid if autouse=True?
First of all, I needed to rename your class to TestABC, so that pytest can find it.
Secondly, I will assume that you are using Python 2, but remember that it will no longer be supported in pytest 5.0.
And what I think is your main issue: since your are using a class to group your tests, the first argument to your methods is always the instance itself (aka self) even if you give it another name.
That's why pytest is not really detecting that you want to pass the fixture and it never does it.
So try something like this:
class TestABC:
def test(self, test_fixture):
print "Doing something"
def test_2(self, test_fixture):
print "Executing second test"
Also note that you don't need to use classes to group your tests, you could use regular functions. So removing the class should also solve your problem:
def test(test_fixture):
print "Doing something"
def test_2(test_fixture):
print "Executing second test"
Also, in your case module and session will produce a similar effect, since you only have one module (at least in your example).
But you can easily compare with the default scope: if you remove the scope argument completely, the fixture will be called once before each test that requires it.
#pytest.fixture
def test_fixture():
# DO something
print "Default test level fixture"
Using module will call it once for the whole module. And session would call it once for all the modules.
To answer your original question: no, scopes also apply when autouse=False.

Override a "private" method in a python module

I want to test a function in python, but it relies on a module-level "private" function, that I don't want called, but I'm having trouble overriding/mocking it. Scenario:
module.py
_cmd(command, args):
# do something nasty
function_to_be_tested():
# do cool things
_cmd('rm', '-rf /')
return 1
test_module.py
import module
test_function():
assert module.function_to_be_tested() == 1
Ideally, in this test I dont want to call _cmd. I've looked at some other threads, and I've tried the following with no luck:
test_function():
def _cmd(command, args):
# do nothing
pass
module._cmd = _cmd
although checking module._cmd against _cmd doesn't give the correct reference. Using mock:
from mock import patch
def _cmd_mock(command, args):
# do nothing
pass
#patch('module._cmd', _cmd_mock)
test_function():
...
gives the correct reference when checking module._cmd, although `function_to_be_tested' still uses the original _cmd (as evidenced by it doing nasty things).
This is tricky because _cmd is a module-level function, and I dont want to move it into a module
[Disclaimer]
The synthetic example posted in this question works and the described issue become from specific implementation in production code. Maybe this question should be closed as off topic because the issue is not reproducible.
[Note] For impatient people Solution is at the end of the answer.
Anyway that question given to me a good point to thought: how we can patch a method reference when we cannot access to the variable where the reference is?
Lot of times I found some issue like this. There are lot of ways to meet that case and the commons are
Decorators: the instance we would like replace is passed as decorator argument or used in decorator static implementation
What we would like to patch is a default argument of a method
In both cases maybe refactor the code is the best way to play with that but what about if we are playing with some legacy code or the decorator is a third part decorator?
Ok, we have the back on the wall but we are using python and in python nothing is impossible. What we need is just the reference of the function/method to patch and instead of patching its reference we can patch the __code__: yes I'm speaking about patching the bytecode instead the function.
Get a real example. I'm using default parameter case that is simple, but it works either in decorator case.
def cmd(a):
print("ORIG {}".format(a))
def cmd_fake(a):
print("NEW {}".format(a))
def do_work(a, c=cmd):
c(a)
do_work("a")
cmd=cmd_fake
do_work("b")
Output:
ORIG a
ORIG b
Ok In this case we can test do_work by passing cmd_fake but there some cases where is impossible do it: for instance what about if we need to call something like that:
def what_the_hell():
list(map(lambda a:do_work(a), ["c","d"]))
what we can do is patch cmd.__code__ instead of _cmd by
cmd.__code__ = cmd_fake.__code__
So follow code
do_work("a")
what_the_hell()
cmd.__code__ = cmd_fake.__code__
do_work("b")
what_the_hell()
Give follow output:
ORIG a
ORIG c
ORIG d
NEW b
NEW c
NEW d
Moreover if we want to use a mock we can do it by add follow lines:
from unittest.mock import Mock, call
cmd_mock = Mock()
def cmd_mocker(a):
cmd_mock(a)
cmd.__code__=cmd_mocker.__code__
what_the_hell()
cmd_mock.assert_has_calls([call("c"),call("d")])
print("WORKS")
That print out
WORKS
Maybe I'm done... but OP still wait for a solution of his issue
from mock import patch, Mock
cmd_mock = Mock()
#A closure for grabbing the right function code
def cmd_mocker(a):
cmd_mock(a)
#patch.object(module._cmd,'__code__', new=cmd_mocker.__code__)
test_function():
...
Now I should say never use this trick unless you are with the back on the wall. Test should be simple to understand and to debug ... try to debug something like this and you will become mad!

Python monkey patch private function

I have a module with a function (call it a()) that calls another function defined in the same module (call it __b()). __b() is a function which speaks to a website via urllib2 and gets some data back. Now I'm trying to test a(), but of course would rather not have my unit tests speak to the public internet. Thus, I'm thinking if I can monkey patch __b() with a function which returns canned data, then I can write the tests for a().
To be more concrete, my module looks kinda like:
def a():
return __b("someval")
def __b(args):
return something_complex_with_args
So now I want to test a(), but I need to monkey patch out __b. The problem is that A) the vast majority of information on monkey patching applies to methods of a class, not to functions in a module, and B) the function I want to monkey patch is private. I am willing to change __b to be non-private if it makes the process more feasible, but would rather not.
Suggestions?
Edit: as it stands the test class looks like:
from unittest import TestCase
import mymodule
def newfn(args):
return {"a" : "b"}
mymodule._b = newfn
class TestMyModule(TestCase):
def test_basic(self):
print(mymodule.a('somearg'))
And when I run this, I see the output if the monkey patching had not been done at all, rather than seeing {'a': 'b'} get printed out.
I can't seem to reproduce your issue (I've tweaked your example a bit, since it doesn't run at all as-is). Did you just mistype something (like mymodule._b instead of mymodule.__b)?
mymodule.py:
def a(x):
return __b("someval")
def __b(args):
return "complex_thingy: {}".format(args)
mytest.py:
from unittest import TestCase
import mymodule
def newfn(args):
return {"a" : "b"}
mymodule.__b = newfn
class TestMyModule(TestCase):
def test_basic(self):
print(mymodule.a('somearg'))
Output:
C:\TEMP>python -m unittest mytest
{'a': 'b'}
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
C:\TEMP>
Seems to work fine.
Or outside of unittest:
mytest2.py:
import mymodule
def newfn(args):
return {"a" : "b"}
mymodule.__b = newfn
print(mymodule.a('somearg'))
Output:
C:\TEMP>python mytest2.py
{'a': 'b'}
C:\TEMP>
If your module was named 'foo', then the following should work.
import foo
def patched_version():
return 'Hello'
foo.__b = patched_version
print (foo.a())
where foo.py is
def a():
return __b()
def __b():
return 'Goodbye'
I was facing the same problem, but finally got to the solution. The problem was when using the monkey patch in the unittest.TestCase class. Here's the solution that works just fine:
If you are using Python 2, you'll need to install the "mock" library (http://www.voidspace.org.uk/python/mock/) using easy_install or some other way. This library is already bundled with Python 3.
Here's what the code looks like:
from mock import patch
class TestMyModule(TestCase):
def test_basic(self):
with patch('mymodule._b') as mock:
mock.return_value={"a" : "b"} # put here what you want the mock function to return. You can make multiple tests varying these values.
#keep the indentation. Determines the scope for the patch.
print(mymodule.a('somearg'))
Although this way apparently seems a bit less convenient compared to making a mock function where we could mimic the actual sub-function, _b(), having logic to return different values based on different arguments, but then we unnecessarily add more chances of error. In this approach we just hard-code what we want our mocked function to return and test the actual function we set out to test, that was a().

Categories

Resources