joel
joel

Reputation: 7868

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

Upvotes: 2

Views: 9608

Answers (4)

Tarifazo
Tarifazo

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

chepner
chepner

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

user10186512
user10186512

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

knh190
knh190

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

Related Questions