Reputation: 7868
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
Upvotes: 2
Views: 9608
Reputation: 4343
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
Upvotes: 0
Reputation: 531165
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
Upvotes: 0
Reputation: 453
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.
Upvotes: 2
Reputation: 2882
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.
Upvotes: 0