Test for 'ExceptionError' with py.test - python

Hello I have an class object dice that eventually deletes itself. Using py.test how can I make py.test return positive to the object deleting itself?
I tried this but it is a syntax error:
assert dice raises(ExceptionError)
Thanks.

With pytest, test for expected errors like this
import pytest
with pytest.raises(ZeroDivisionError):
1 / 0
that's an example from the docs, chapter Assertions about expected exceptions
In your case it would be something like this
import pytest
with pytest.raises(ExceptionError)
# Instantiate a dice object
# Do stuff that makes it delete itself
Here I used the error that you yourself named, I don't know if that is the actual error that is raised.

You could check globals(). It contains all global variables.
if "dice" in globals():
do something
else:
raise(Exception)

Related

Pytest mock / patch of an api call

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.

Python mock not applied to object under test

I am attempting to mock an object to perform some testing
test.py
#patch("api.configuration.client.get_configuration")
def test(mock_client_get_configuration):
mock_client_get_configuration.return_value = "123"
result = code_under_test()
assert result
Inside code_under_test() I make the concrete call to get_configuration().
code.py
from api.configuration.client import get_configuration
def code_under_test():
config = get_configuration("a", "b")
However, whenever I run my test, get_configuration is always the concrete version and not my mock.
If I add a print in my test, I can see that mock_client_get_configuration is a MagicMock.
If I add a print inside code_under_test, then get_configuration is always the actual <function.... and not my mock.
I feel somewhere I am going wrong in how I create my mock. Perhaps it is because my mock does not mimic the two parameters needed on the concrete call so the signature is incorrect?
I'm using Python 3.9 and pytest 7.0.1.

Django mocks not working as expected

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)

How to disable a try/except block during testing?

I wrote a cronjob that iterates through a list of accounts and performs some web call for them (shown below):
for account in self.ActiveAccountFactory():
try:
self.logger.debug('Updating %s', account.login)
self.update_account_from_fb(account)
self.check_balances()
self.check_rois()
except Exception,e:
self.logger.exception(traceback.format_exc())
Because this job is run by heroku one every 10 minutes, I do not want the entire job to fail just because one account is running into issues (it happens). I placed a try catch clause here so that this task is "fault-tolerant".
However, I noticed that when I am testing, this try/catch block is giving me cryptic problems because of the task is allowed to continue executing even though there is some serious error.
What is the best way to disable a try/except block during testing?
I've though about implementing the code directly like this:
for account in self.ActiveAccountFactory():
self.logger.debug('Updating %s', account.login)
self.update_account_from_fb(account)
self.check_balances()
self.check_rois()
self.logger.exception(traceback.format_exc())
in my test cases but then this makes my tests very clumsy as I am copying large amounts of code over.
What should I do?
First of all: don't swallow all exceptions using except Exception. It's bad design. So cut it out.
With that out of the way:
One thing you could do is setup a monkeypatch for the logger.exception method. Then you can handle the test however you see fit based on whether it was called, whether it's creating a mock logger, or a separate testing logger, or a custom testing logger class that stops the tests when certain exceptions occur. You could even choose to end the testing immediately by raising an error.
Here is an example using pytest.monkeypatch. I like pytest's way of doing this because they already have a predefined fixture setup for it, and no boilerplate code is required. However, there are others ways to do this as well (such as using unittest.mock.patch as part of the unitest module).
I will call your class SomeClass. What we will do is create a patched version of your SomeClass object as a fixture. The patched version will not log to the logger; instead, it will have a mock logger. Anything that happens to the logger will be recorded in the mock logger for inspection later.
import pytest
import unittest.mock as mock # import mock for Python 2
#pytest.fixture
def SomeClassObj_with_patched_logger(monkeypatch):
##### SETUP PHASE ####
# create a basic mock logger:
mock_logger = mock.Mock(spec=LoggerClass)
# patch the 'logger' attribute so that when it is called on
# 'some_class_instance' (which is bound to 'self' in the method)
# things are re-routed to mock_logger
monkeypatch.setattr('some_class_instance.logger', mock_logger)
# now create class instance you will test with the same name
# as the patched object
some_class_instance = SomeClass()
# the class object you created will now be patched
# we can now send that patched object to any test we want
# using the standard pytest fixture way of doing things
yield some_class_instance
###### TEARDOWN PHASE #######
# after all tests have been run, we can inspect what happened to
# the mock logger like so:
print('\n#### ', mock_logger.method_calls)
If call.exception appears in the method calls of the mock logger, you know that method was called. There are a lot of other ways you could handle this as well, this is just one.
If you're using the logging module, LoggerClass should just be logging.Logger. Alternatively, you can just do mock_logger = mock.Mock(). Or, you could create your own custom testing logger class that raises an exception when its exception method is called. The sky is the limit!
Use your patched object in any test like so:
def test_something(SomeClassObj_with_patched_logger):
# no need to do the line below really, just getting
# a shorter variable name
my_obj = SomeClassObj_with_patched_logger
#### DO STUFF WITH my_obj #####
If you are not familiar with pytest, see this training video for a little bit more in depth information.
try...except blocks are difficult when you are testing because they catch and try to dispose of errors you would really rather see. As you have found out. While testing, for
except Exception as e:
(don't use Exception,e, it's not forward-compatible) substitute an exception type that is really unlikely to occur in your circumstances, such as
except AssertionError as e:
A text editor will do this for you (and reverse it afterwards) at the cost of a couple of mouse-clicks.
You can make callables test-aware by add a _testing=False parameter. Use that to code alternate pathways in the callable for when testing. Then pass _testing=True when calling from a test file.
For the situation presented in this question, putting if _testing: raise in the exception body would 'uncatch' the exception.
Conditioning module level code is tricker. To get special behavior when testing module mod in package pack, I put
_testing = False # in `pack.__init__`
from pack import _testing # in pack.mod
Then test_mod I put something like:
import pack
pack._testing = True
from pack import mod

Is there a way to override default assert in pytest (python)?

I'd like to a log some information to a file/database every time assert is invoked. Is there a way to override assert or register some sort of callback function to do this, every time assert is invoked?
Regards
Sharad
Try overload the AssertionError instead of assert. The original assertion error is available in exceptions module in python2 and builtins module in python3.
import exceptions
class AssertionError:
def __init__(self, *args, **kwargs):
print("Log me!")
raise exceptions.AssertionError
I don't think that would be possible. assert is a statement (and not a function) in Python and has a predefined behavior. It's a language element and cannot just be modified. Changing the language cannot be the solution to a problem. Problem has to be solved using what is provided by the language
There is one thing you can do though. Assert will raise AssertionError exception on failure. This can be exploited to get the job done. Place the assert statement in Try-expect block and do your callbacks inside that block. It isn't as good a solution as you are looking for. You have to do this with every assert. Modifying a statement's behavior is something one won't do.
It is possible, because pytest is actually re-writing assert expressions in some cases. I do not know how to do it or how easy it is, but here is the documentation explaining when assert re-writing occurs in pytest:
https://docs.pytest.org/en/latest/assert.html
By default, if the Python version is greater than or equal to 2.6, py.test rewrites assert statements in test modules.
...
py.test rewrites test modules on import. It does this by using an
import hook to write a new pyc files.
Theoretically, you could look at the pytest code to see how they do it, and perhaps do something similar.
For further information, Benjamin Peterson wrote up Behind the scenes of py.test’s new assertion rewriting [ at http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html ]
I suggest to use pyhamcrest. It has very beatiful matchers which can be simply reimplemented. Also you can write your own.

Categories