Can assertions compare against print statments? - python

Can assertions compare against print statements in python like this:
def printname(name):
print(name)
#to not raise an error:
assert printname("Hello") == "Hello"
#to raise an error:
assert printname("Hello") == "notHello"

No, assertions can't do that. If you want to do that try this:
def printname(name):
print(name)
return name
#to not raise an error:
assert printname("Hello") == "Hello"
#to raise an error:
assert printname("Hello") == "notHello"
This technically doesn't compare it, but its a valid workaround. If you really want to compare them take a look into StringIO.

Try using mock + stringIO to capture stdout/err:
from mock import patch
from StringIO import StringIO
def test_foobar():
out, err = StringIO(), StringIO()
with patch.multiple(sys, stdout=out, stderr=err):
do_stuff()
assert out.getvalue() == 'foo'
assert err.getvalue() == 'bar'
Also if you are doing this within a test framework, you may be able to do a variety of things. In particular for pytest:
def test_myoutput(capsys): # or use "capfd" for fd-level
print("hello")
sys.stderr.write("world\n")
captured = capsys.readouterr()
assert captured.out == "hello\n"
assert captured.err == "world\n"
print("next")
captured = capsys.readouterr()
assert captured.out == "next\n"

No. The function printname doesn't have an explicit return statement, so it return the value . So the assert compares None against a string and of course it raise an assertion error.

Related

How to check that print was called in a function?

Suppose f conditionally calls print; I'd like to know whether this happens within test_*(). How can this be accomplished?
Example:
def f(integer): # defined in and imported from separate module
if isinstance(integer, str):
print("WARNING: integer is str")
def test_f():
f("5")
assert print.called
Attempted approach:
def tracked_call(self, *args, **kwargs):
self.called = True
self.__call__(*args, **kwargs)
print.__call__ = tracked_call
>>> AttributeError: 'builtin_function_or_method' object attribute '__call__' is read-only
Solution 1 (best): check that print was called, and that it prints specific text; doesn't use a fixture:
import builtins
import contextlib, io
from unittest.mock import Mock
def test_f():
mock = Mock()
mock.side_effect = print # ensure actual print is called to capture its txt
print_original = print
builtins.print = mock
try:
str_io = io.StringIO()
with contextlib.redirect_stdout(str_io):
f("5")
output = str_io.getvalue()
assert print.called # `called` is a Mock attribute
assert output.startswith("WARNING:")
finally:
builtins.print = print_original # ensure print is "unmocked"
(If print in f writes to sys.stderr instead of the default sys.stdout, use contextlib.redirect_stderr.)
Solution 2: check that print prints specific text within call; from docs:
def test_f(capsys):
f("5")
out, err = capsys.readouterr()
assert out.startswith("WARNING:")
This assuming the default print(file=sys.stdout), else the string of interest is in err. If specific text is of no interest, can do assert out or err to verify that something was printed. This doesn't necessarily test whether print was called, as we can do print(end='').

How to assert all asserts failed in python

I'm trying to write examples of how NOT to write tests in pytest. For this, I'd like to be able to somehow mod test functions so that they fail if any assert passes. This is easy with one assert using a decorator:
def faulty_test(test_fn):
def new_test_fn():
try:
test_fn()
except AssertionError:
pass
else:
raise AssertionError
return new_test_fn
#faulty_test
def test_sth():
assert 1 == 2
but I'd like to do it for a function with any number of asserts. The test should fail if any of the asserts pass. It doesn't have to be a decorator
Perhaps you should think about it this way: At the point of the exception, execution leaves the try block and enters the except block. You cannot, at least in any simple way I can think of, re-enter the try block from within the except block.
And perhaps I'm misunderstanding the question, but couldn't you simply replace your assertions with the inverse of those assertions? If you want to assert that a thing is false, you can:
assert not (1 == 2)
If for some reason you're dealing with a situation where you assume that the given functions will, for some reason, always fail their asserts and you cannot change that, there does not seem to be any way to ensure all the asserts run, since the design of assertions is to intentionally abort immediately on failure.
I guess it doesn't have to be multiple asserts. You can write:
assert a == b or c == d or # so on
Any condition is True will cause the assertion to pass.
If you "number" the assertions, catching an assertion with a value greater than 0 implies the previous assertions passed.
def faulty_test(test_fn):
def new_test_fn():
try:
test_fn()
except AssertionError as exc:
if exc.args[0] > 0:
# Assertions 0 through exc.args[0]-1 all passed
raise AssertionError
else:
raise AssertionError
return new_test_fn
#faulty_test
def test_sth():
assert 1 == 2, 0
assert 2 == 3, 1
assert 3 == 4, 2
You should probably handle that in the function code by using a _debug or other (hidden) argument. Then you can use a decorator on that. What I would do:
def deco(inF):
def wrapper(*args, **kwargs):
kwargs['_debug'] = True
output = inF(*args, **kwargs)
#print(output)
for i, _ in enumerate(zip(*output)):
condition, msg = _
# this will raise the first passed assert. Otherwise loop again to print all "passed" assertions, then raise
if not condition:
raise AssertionError('Condition {} succeded: {} is False'.format(1 + i, msg))
return output
return wrapper
#deco
def f(i, j , k, _debug=False):
cond1, msg1 = i == 1, 'i is not 1'
cond2, msg2 = j == 2, 'j is not 2'
cond3, msg3 = k == 3, 'k is not 3'
assert cond1 or _debug, msg1
assert cond2 or _debug, msg2
assert cond3 or _debug, msg3
if _debug:
return (cond1, cond2, cond3), (msg1, msg2, msg3)
return i + j + k
f(1,1,5)
>>AssertionError: Condition 2 succeded: j is not 2 is False

py.test assert may raise, and if it raises it will be __

Is there pytest functionality similar to pytest.raises that passes iff the block raises the specified exception, or doesn't raise at all? Something like:
def test_encode_err(ulist):
with pytest.maybe_raises_but_only(UnicodeEncodeError): # <== ?
assert encode_list(ulist, 'ascii') == map(lambda x:x.encode('ascii'), ulist)
This question came up in the following situation..
The function to test:
def encode_list(lst, enc):
"Encode all unicode values in ``lst`` using ``enc``."
return [(x.encode(enc) if isinstance(x, unicode) else x) for x in lst]
A couple of simple tests (fixtures below):
def test_encode_err(ulist):
with pytest.raises(UnicodeEncodeError):
assert encode_list(ulist, 'ascii')
def test_encode_u8(ulist, u8list):
assert encode_list(ulist, 'u8') == u8list
The fixtures:
#pytest.fixture(
scope='module',
params=[
u'blåbærsyltetøy',
u'', # <==== problem
]
)
def ustr(request):
print 'testing with:', `request.param`
return request.param
#pytest.fixture
def u8str(ustr):
return ustr.encode('u8')
#pytest.fixture
def ulist(ustr):
return [ustr, ustr]
#pytest.fixture
def u8list(u8str):
return [u8str, u8str]
the indicated <== problem is only a problem for test_encode_err() (and not test_encode_u8()), and happens since u''.encode('ascii') doesn't raise a UnicodeEncodeError (no unicode strings that doesn't contain characters above code point 127 will raise).
Is there a py.test function that covers this use case?
If you don't care when the exception is thrown just write the code as normal but put a try...except block round it to ignore the error.
def test_encode_err(ulist):
try:
assert encode_list(ulist, 'ascii') == map(lambda x:x.encode('ascii'), ulist)
except UnicodeDecodeError:
pass
Really though consider whether you should be writing a test at all if you don't know whether the code will throw an exception. Try pinning down the data a bit more and having two tests, one which raises the exception and one which doesn't.
I consider the provided response really incomplete. I like to parametrize tests for functions that could accepts different values.
Consider the following function that only accepts empty strings, in which case returns True. If you pass other type raises a TypeError and if the passed string is not empty a ValueError.
def my_func_that_only_accepts_empty_strings(value):
if isinstance(value, str):
if value:
raise ValueError(value)
return True
raise TypeError(value)
You can conveniently write parametric tests for all cases in a single test in different ways:
import contextlib
import pytest
parametrization = pytest.mark.parametrize(
('value', 'expected_result'),
(
('foo', ValueError),
('', True),
(1, TypeError),
(True, TypeError),
)
)
#parametrization
def test_branching(value, expected_result):
if hasattr(expected_result, '__traceback__'):
with pytest.raises(expected_result):
my_func_that_only_accepts_empty_strings(value)
else:
assert my_func_that_only_accepts_empty_strings(
value,
) == expected_result
#parametrization
def test_without_branching(value, expected_result):
ctx = (
pytest.raises if hasattr(expected_result, '__traceback__')
else contextlib.nullcontext
)
with ctx(expected_result):
assert my_func_that_only_accepts_empty_strings(
value,
) == expected_result
Note that when an exception raises inside pytest.raises context, the contexts exits so the later assert ... == expected_result is not executed when the exception is catch. If other exception raises, it is propagated to your test so the comparison is not executed either. This allows you to write more assertions after the execution of the function for successfull calls.
But this can be improved in a convenient maybe_raises fixture, that is what you're looking for at first:
#contextlib.contextmanager
def _maybe_raises(maybe_exception_class, *args, **kwargs):
if hasattr(maybe_exception_class, '__traceback__'):
with pytest.raises(maybe_exception_class, *args, **kwargs):
yield
else:
yield
#pytest.fixture()
def maybe_raises():
return _maybe_raises
And the test can be rewritten as:
#parametrization
def test_with_fixture(value, expected_result, maybe_raises):
with maybe_raises(expected_result):
assert my_func_that_only_accepts_empty_strings(
value,
) == expected_result
Really nice, right? Of course you need to know how the magic works to write the test properly, always knowing that the context will exits when the exception is catched.
I think that pytest does not includes this because could be a really confusing pattern that could lead to unexpected false negatives and bad tests writing. Rather than that, pytest documentation encourauges you to pass expectation contexts as parameters but for me this solution looks really ugly.
EDIT: just packaged this fixture, see pytest-maybe-raises.

How to capture assert in python script

Is there any way in Python2.7, we can capture and log the assert statements in general python scripting despite of assert is True or False
Suppose I assert following line in code:
assert len(lst) ==4
So is any way can log what statement is passed, at what line and it is true or false. I do not want to use a wrapper function, looking for something inbuilt in python .
Note: What I want to achieve is , suppose I have legacy code having 1000's of assert statements , without changing the code, I should be able to log which assert statement is executed and what is the output, Is its achievable in python 2.7.
try:
assert len(lst) == 4
print "True"
except AssertionError as e:
print "False"
Yes, you can define a custom excepthook to log some extra information:
import sys
import logging
def excepthook(*args):
logging.getLogger().error('Uncaught exception:', exc_info=args)
sys.excepthook = excepthook
assert 1==2
EDIT: Whoops I forgot you wanted to log even if it's true :) oh well I'll leave this for a little bit in case it informs you or someone else...
This is what I could achieve closet, as does not seem it is possible in Python2.7 . I created a wrapper function.
import inspect
def assertit(condition,message):
# The caller method
(frame, filename, line_number,function_name, lines, index) = inspect.getouterframes(inspect.currentframe())[1]
detailed_message=" => {message} [ {filename} : {line_number}]".format(message=message,line_number=line_number,
filename=filename)
if condition:
print "True %s"%detailed_message
return
raise AssertionError("False: %s"%detailed_message)
assertit(1==1,"Check if 1 equal 1")
assertit(1==2,"Check if 1 equal 2")
### HERE IS THE OUTPUT
True => Check if 1 equal 1 [ test_cases.py : 20]
Traceback (most recent call last):
File "test_cases.py", line 21, in <module>
assertit(1==2,"Check if 1 equal 2")
File "test_cases.py", line 19, in assertit
raise AssertionError("False: %s"%detailed_message)
AssertionError: False: => Check if 1 equal 2 [ test_cases.py : 21]
This is a proof of concept. So please downvote, or I will delete it...
The idea is to replace the assert statement with print statement at the execution.
import ast
import inspect
from your_module import main
def my_assert(test_result, msg):
assert test_result, msg
# if test_result is True, just print the msg
return "assert: {}, {}".format(test_result, msg)
class Transformer(ast.NodeTransformer):
def visit_Assert(self, node):
f = ast.Name(id='my_assert', ctx=ast.Load())
c = ast.Call(func=f, args=[node.test, node.msg], keywords=[])
p = ast.Print(values=[c], nl=True)
# set line#, column offset same as the original assert statement
f.lineno = c.lineno = p.lineno = node.lineno
f.col_offset =c.col_offset = p.col_offset = node.col_offset
return p
def replace_assert_with_print(func):
source = inspect.getsource(func)
ast_tree = ast.parse(source)
Transformer().visit(ast_tree)
return compile(ast_tree, filename="<ast>", mode="exec")
if __name__ == '__main__':
exec(replace_assert_with_print(main))
main(4)
And here is your_module.py
def main(x):
assert x == 4, "hey {} is not 4".format(x)
return x

Use decorators to wrap all functions with "if func returned false, return false"

I'm writing a very basic Python script based on a main function that sequentially calls other functions.
What I would like to do is to wrap all of the functions that are called from main with something like:
result = func(*some_args):
if (result != True):
return result
For example, for this code:
def func1(arg1 , arg2):
if (some_cond):
return False #Or some err_val
return True
def func2(arg1):
if (some_cond):
return False
return True
def main():
func1(val1 , val2)
func2(val3)
return True
if __name__ == "__main__":
import sys
result = main()
if (result == err_val1):
# Do something. Maybe print. Maybe call some function.
sys.exit(1)
I want that if one of the functions fails main would break and return its error. Can I do this using decorators?
I maintain that the best solution would be the use of exceptions. If that's absolutely not what you want, you can do some short-circuiting:
return func1() and func2()
To extend this to more functions without a ton of ands:
from functools import partial
def main():
funcs = (partial(func1, arg1, arg2),
partial(func2, arg1))
if any(!f() for f in funcs):
return False
Though this doesn't return "its error" (the failed function's error), it just returns False. If you want to differentiate more between different kinds of errors... well, you're back to exceptions.
This is precisely what exceptions are built for in Python.
# imports belong at the *top* of the file
import sys
class SomeDescriptiveError(Exception): pass
class SomeOtherSpecialError(Exception): pass
def func1(arg1 , arg2):
if (some_cond):
raise SomeDescriptiveError('Cannot frobnosticate the fizzbuzz')
return arg1 + arg2
# or skip the return statement altogether
def func2(arg1):
if (some_cond):
raise SomeOtherSpecialError('The frontobulator is no longer cromulent')
return ''.join(reversed(arg1))
def main():
print(func1(val1 , val2))
print(func2(val3))
if __name__ == "__main__":
try:
result = main()
except SomeDescriptiveError as e:
print('Oh dear')
sys.exit(e.args[0])
except SomeOtherSpecialError as e:
print('Oh no')
sys.exit(e.args[0])
else:
print('All systems are fully operational')
finally:
print('I really should clean up all these bits.')
Since you do actually want the program to die when one of these errors occurs you might as well raise SystemExit. Here's a way to do it with a decorator.
flag = 2
def die_on_not_True(func):
def wrapper(*args):
rc = func(*args)
if rc is not True:
fmt = 'Function {} failed with return value {!r}'
print(fmt.format(func.__name__, rc))
raise SystemExit(1)
return True
return wrapper
#die_on_not_True
def func1(arg1 , arg2):
if arg1 == flag:
return 'error 1'
return True
#die_on_not_True
def func2(arg1):
if arg1 == flag:
return 'error 2'
return True
def main():
val1, val2, val3 = 1, 2, 3
print(func1(val1, val2))
print('one')
print(func2(val3))
print('two')
if __name__ == '__main__':
main()
output
True
one
True
two
If we set flag = 1, the output becomes
Function func1 failed with return value 'error 1'
If we set flag = 3, the output becomes
True
one
Function func2 failed with return value 'error 2'
When flag equals 2, the exit status of 0 is returned to the shell, when flag equals 1 or 3, the exit status of 1 is returned.
If you want to do further processing after printing the error message, then raise a custom exception instead of SystemExit and catch it by wrapping your main call in a try...except.
I guess, what you really want is a universal exception catcher, that would catch and return the exception of any wrapped function. You can easily do it this way.
def return_exception(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
return e
return wrapper
Example
In [3]: #return_exception
...: def div(a, b):
...: return a / b
...:
In [4]: div(1, 0)
Out[4]: ZeroDivisionError('division by zero')
So then you can process the return exception object the way you want, though it's pretty hard to say why you need that.
Update As others have noted it's generally good to only catch particular exceptions. You can modify the decorator slightly.
def return_exception(*exception_types):
def build_wrapper(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except exception_types as e:
return e
return wrapper
return build_wrapper
Example:
In [6]: #return_exception(ZeroDivisionError)
...: def div(a, b):
...: return a / b
...:
In [7]: div(0, 1)
Out[7]: 0.0
In [8]: div(1, 0)
Out[8]: ZeroDivisionError('division by zero')
In [9]: div(1, "a")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
...
TypeError: unsupported operand type(s) for /: 'int' and 'str'
In [10]: #return_exception(ZeroDivisionError, TypeError)
....: def div(a, b):
....: return a / b
....:
In [11]: div(1, 0)
Out[11]: ZeroDivisionError('division by zero')
In [12]: div(1, "a")
Out[12]: TypeError("unsupported operand type(s) for /: 'int' and 'str'")
As you see, you only catch the specified exceptions (you can still specify the universal Exception class, though).

Categories

Resources