I am trying to understand patching, but I seem to be failing to do so.
Currently I am trying to patch an api call inside of the tested function:
# function being tested
def tested_function():
response = call_to_api()
status = response["status"]
if status == "something":
# some more logic to test
In the test_file.py I attempt to do the following:
#patch("import_from_same_file.call_to_api")
def test_tested_function(my_mock):
my_mock.return_value = {"status":"COMPLETE"}
All I've been able to achieve so far is Got error: list indices must be integers or slices, not str error with no clue where is it actually coming from. Please help, already spent so many hours on this.
I have also tried to supply an object as a return value of the mock.
class Response():
status = "COMPLETE"
With no luck though. Apparently I am missing something about how the patching works.
It's hard to say exactly what is going wrong, without seeing the call stack, but the following general pattern works.
foo.py
def foo():
pass
def bar():
result = foo()
return result["status"]
The with test_foo.py as
import mock
from foo import bar
#mock.patch("foo.foo", return_value={"status": "PASSED"})
def test_add(mock_foo):
assert bar() == "PASSED"
This general pattern works with pytest test_foo.py in the same directory.
Related
I have this python lambda on handler.py
def isolate_endpoints(event=None, context=None):
endpoint_id = event['event']['endpoint_id']
client = get_edr_client()
response = client.isolate_endpoints(endpoint_id=endpoint_id)
return response # E.g {"reply":{"status":"success", "error_msg":null}}
I want to write a unit test for this lambda. However after reading on the unittests and having seen actual implementations of the tests I can comfortably say I have no idea what is being tested exactly. (I know the theory but having hard time understanding the implementation of mocks, magicmocks etc.)
The unittest that I have right now is on test_calls and looks like this:
#mock.patch('project_module.utils.get_edr_client')
def test_isolate_endpoints(get_edr_client: MagicMock):
client = MagicMock()
get_edr_client.return_value = client
mock_event = {"event": {"endpoint_id":"foo"}}
resp = isolate_endpoints(event=mock_event) # Is it right to call this lambda directly from here?
assert resp is not None
expected = [call()] ## what is this ?? # What is supposed to follow this?
assert client.isolate_endpoints.call_args_list == expected
definition & body of get_edr_client in utils.py:
from EDRAPI import EDRClient
def get_edr_client():
return EDRClient(api_key="API_KEY")
I'll try to explain each aspect of the test you have written
#mock.patch('project_module.utils.get_edr_client')
This injects dependencies into your test. You are essentially patching the name 'project_module.utils.get_edr_client' with an auto-created MagicMock object that's passed to you as the test argument get_edr_client. This is useful to bypass external dependencies in your test code. You can pass mock implementations of objects that are used in your code that's under test, but don't need to be tested in this test itself.
def test_isolate_endpoints(get_edr_client: MagicMock):
client = MagicMock()
get_edr_client.return_value = client
Setup: You are setting up the external mock you patched into the test. Making it return more mock objects so that the code under test works as expected.
mock_event = {"event": {"endpoint_id":"foo"}}
resp = isolate_endpoints(event=mock_event)
Invoke: Calling the code that you want to test, with some (possibly mock) argument. Since you want to test the function isolate_endpoints, that's what you should be calling. In other tests you could also try passing invalid arguments like isolate_endpoints(event=None) to see how your code behaves in those scenarios.
assert resp is not None
Verify: Check if the value returned from your function is what you expect it to be. If your function was doing 2+2 this is the part where you check if the result equals 4 or not.
expected = [call()]
assert client.isolate_endpoints.call_args_list == expected
Verify side-effects: Your function has side-effects apart from returning a value. So you should check if those are happening properly or not. You expect the function to call isolate_endpoint on the client mock you injected earlier once. So you're checking if that method was called once with no arguments. The call() is for matching one invocation of the method with no arguments. The [call()] means only one invocation with no arguments. Then you just check if that is indeed the case with the assertion.
A much cleaner way to do the same this is to do this instead:
client.isolate_endpoints.assert_called_once()
Which does the same thing.
I'm struggling with django mock; I have even simplified an unit test but the test is still failing. I want to verify that a method is called (even with any parameter), but the "assert_called_once_with" always returns False.
Currently I'm trying:
#patch('utils.make_reset_password')
def test_shouldHaveCalledMakeResetToken(self, mocked):
user = User.get(...)
make_reset_password(user)
mocked.assert_called_once_with(user)
Even this simple example is failing with:
AssertionError: Expected 'make_reset_password' to be called once. Called 0 times
How this is possible? What am I doing wrong?
Thanks in advance
You have to use full path to utils, e.g. #patch('my_app.utils.make_reset_password') and then in the test call a function that calls make_reset_password.
#patch('my_app.utils.make_reset_password')
def test_shouldHaveCalledMakeResetToken(self, mock_make_reset_password):
user = User.get(...)
function_under_test(user)
mock_make_reset_password.assert_called_once_with(user)
EDIT
The other thing that comes to my mind is you are not mocking the correct function. If make_reset_password is imported from utils in another module then you need to change the path in the #patch decorator.
For example
# my_module.py
from my_app.utils import make_reset_password
def run_make_reset_password(user):
make_reset_password(user)
# tests.py
#patch('my_app.my_module.make_reset_password')
def test_shouldHaveCalledMakeResetToken(self, mock_make_reset_password):
user = User.get(...)
run_make_reset_password(user)
mock_make_reset_password.assert_called_once_with(user)
In pytest (3.04; python 3.4) I'm trying to disable output capture under certain circumstances. I'm trying to follow the example on this doc page. However, I'm unable to specify capsys as a funcarg. As a follow-up, I'd like to accept both funcarg and on-funcarg arguments so that I can use a class method that takes one or more normal arguments. How can I do that?
Ideally, this class method would work:
def always_print(self, message, capsys):
with capsys.disabled():
print(message)
But, I can't even get this to work:
def always_print(capsys):
with capsys.disabled():
print('FIXME')
Getting the error:
...
> always_print()
E TypeError: always_print() missing 1 required positional argument: 'capsys'
Edit 1:
Piotr's answer solved my specific issue. However, I also discovered two important caveats that I hadn't picked up in the documentation or other posts, so sharing here for others's benefit:
it appears that capsys.disabled() only applies to stdout and not stderr, which is where I was originally sending my debug messages per *nix best practice.
If you set a file handle to sys.stdout before calling capsys.disabled(), then due to the magic file discriptor mangling that pytest does, this will not work.
So, for example, you'll need to do it this way (say, if your kwargs may contain an optional "file" keyword, like the built-in print() does:
fhandle = kwargs.get('file', sys.stdout) #will not work!
with capsys.disabled():
fhandle = kwargs.get('file', sys.stdout) #must be in context
print(message, file=fhandle)
Well, capsys is a build-in fixture for tests. You should get it as a test's argument and pass it further
def always_print(capsys):
with capsys.disabled():
print('FIXME')
def test_always_print(capsys):
always_print(capsys)
It will work if you run it with pytest command.
Edit:
To avoid verbosity, you can prepare some global capsys variable for all tests (based on the answer how to share a variable across modules for all tests in py.test):
# globals.py
capsys = None
# conftest.py
import pytest
import globals as gbl
from _pytest.capture import capsys
#pytest.fixture(autouse=True)
def populate_globals(request):
gbl.capsys = capsys(request)
# my_tests.py
import globals as gbl
def test_foo():
with gbl.capsys.disabled():
print('You can see me')
def test_bar():
with gbl.capsys.disabled():
print('You can see me too')
I am not if this is right to but, I have this so far and I am trying to write unittest for this.
def ValidateInputs(self, class_column_name,):
class_column_name_ok = type(class_column_name) is str
if not class_column_name_ok:
raise(TypeError("Argument class_column_name must be a string type"))
I did this for this unittest but again with not having enough knowledge I am not sure. Any help will be much appreciated
def testClassColumnName(self):
self.assertTrue(type(class_column_name), "str")
Without knowing what you do with these values, I can't say 100%. I'll assume you bind them to the class, then provide the unittests I'd write.
Application code:
class MyClass(object):
"""This is my object, this is what it does"""
def validate_inputs(self, merge_columns, class_column_name):
"""some handy docstring"""
if not isinstance(class_column_name, str):
raise TypeError('Argument class_column_name must be a string, supplied {0}'.format(type(class_column_name))
self.class_column_name = class_column_name
unittests (using unittest form the stdlib):
import unittest
class TestMyClass(unittest.TestCase):
def setUp(self):
self.myclass = MyClass() # cheap way to always have a 'clean class' for testing
def test_validate_input_type_string(self):
"""Happy path test when input is correct, and everything works"""
self.myclass.validate_input(merge_columns='some-columns', class_column_name='some_column_name')
self.assertEqual(True, isinstance(self.myclass.class_column_name, str))
def test_validate_input_raises_typerror(self):
"""We raise TypeError if input != string"""
self.assertRaises(TypeError,
self.myclass.validate_input,
merge_columns=1234,
class_column_name=4321)
if __name__ == '__main__':
unittest.main()
General tips for unittesting:
A) Use self.assertEqual(A, B) -> the output on failure will give you some clue on why it failed. Using something like self.assertTrue normaly just outputs an error message like "False is not True"; which is, while 100% accurate, not very useful.
B) Supplying all positional args as key-word args -> makes reading the test later easier.
C) One assert per test case (two at the very most) -> more than this tends to get your test code too complex. Test should be so simple that anyone (even that intern that was just hired from a 3 month coding bootcamp) can figure them out. It's really painful to rewrite a hole test suite (or spend hours updating test code) b/c of a 10 minute update to application code.
Hope this is helpful.
I have a python module/script which does a few of these
At various nested levels inside the script I take command line inputs, validate them, apply sensible defaults
I also check if a few directories exist
The above are just two examples. I am trying to find out what is the best "strategy" to test this. What I have done is that I have constructed wrapper functions around raw_input and os.path.exists in my module and then in my test I override these two functions to take input from my array list or do some mocked behaviour. This approach has the following disadvantages
Wrapper functions just exist for the sake of testing and this pollutes the code
I have to remember to use the wrapper function in the code everytime and not just call os.path.exists or raw_input
Any brilliant suggestions?
The short answer is to monkey patch these system calls.
There are some good examples in the answer to How to display the redirected stdin in Python?
Here is a simple example for raw_input() using a lambda that throws away the prompt and returns what we want.
System Under Test
$ cat ./name_getter.py
#!/usr/bin/env python
class NameGetter(object):
def get_name(self):
self.name = raw_input('What is your name? ')
def greet(self):
print 'Hello, ', self.name, '!'
def run(self):
self.get_name()
self.greet()
if __name__ == '__main__':
ng = NameGetter()
ng.run()
$ echo Derek | ./name_getter.py
What is your name? Hello, Derek !
Test case:
$ cat ./t_name_getter.py
#!/usr/bin/env python
import unittest
import name_getter
class TestNameGetter(unittest.TestCase):
def test_get_alice(self):
name_getter.raw_input = lambda _: 'Alice'
ng = name_getter.NameGetter()
ng.get_name()
self.assertEquals(ng.name, 'Alice')
def test_get_bob(self):
name_getter.raw_input = lambda _: 'Bob'
ng = name_getter.NameGetter()
ng.get_name()
self.assertEquals(ng.name, 'Bob')
if __name__ == '__main__':
unittest.main()
$ ./t_name_getter.py -v
test_get_alice (__main__.TestNameGetter) ... ok
test_get_bob (__main__.TestNameGetter) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Solution1: I would do something like this beacuse it works:
def setUp(self):
self._os_path_exists = os.path.exists
os.path.exists = self.myTestExists # mock
def tearDown(self):
os.path.exists = self._os_path_exists
It is not so nice.
Solution2: Restructuring your code was not an option as you said, right?
It would make it worse to understand and unintuitive.
Johnnysweb is spot on with what you need to do, but instead of rolling your own, you can import and use mock. Mock is specifically designed for unit testing, and makes it extremely simple to do what you're trying to do. It's built-in to Python 3.3.
For example, if want to run a unit test that replaces os.path.isfile and always returns True:
try:
from unittest.mock import patch
except ImportError:
from mock import patch
class SomeTest(TestCase):
def test_blah():
with patch("os.path.isfile", lambda x: True):
self.assertTrue(some_function("input"))
This can save you a LOT of boilerplate code, and it's quite readable.
If you need something a bit more complex, for example, replacing supbroccess.check_output, you can create a simple helper function:
def _my_monkeypatch_function(li):
x,y = li[0], li[1]
if x == "Reavers":
return "Gorram"
if x == "Inora":
return "Shiny!"
if x == y:
return "The Ballad of Jayne"
def test_monkey():
with patch("subprocess.check_output", _my_monkeypatch_function):
assertEquals(subprocess.check_output(["Mudder","Mudder"]),
"The Ballad of Jayne")