1

After checking few python open source projects I found examples where same function have test cases with assert <> and assert <> is True.

And I cannot find answer about best practices of using assert statement with and without full condition.

For example function

def is_even(number):
    if number % 2 == 0:
        return True
    else:
        return False

can be tested in such way

assert is_even(6) is True 
assert is_even(6) == True

but also it can be tested like

assert is_even(6)

And here I see problem cause if function is_even will be changed on

def is_even(number):
    if number % 2 == 0:
        return <>
    else:
        return False

Where <> can be changed on any number(except 0) or non empty string - last test will pass. But this will be another function.

I saw this and this SO question about assert usage but seems they don't fully cover my question.

From another side PEP 8 specification have such example

  • Don't compare boolean values to True or False using ==

    Yes:   if greeting:
    No:    if greeting == True:
    Worse: if greeting is True:
    

But is it also about assert usage?

chinskiy
  • 2,557
  • 4
  • 21
  • 40
  • 2
    Yes, that PEP-8 advice about booleans also applies to `assert` usage. Similarly, that function should be written as: `def is_even(number):` `return number % 2 == 0` – PM 2Ring Oct 08 '18 at 10:54
  • 3
    What are you actually asking? – DeepSpace Oct 08 '18 at 10:56
  • 1
    @DeepSpace question is about is it good practice to use for tests just `assert is_even(6)` or better to use `assert is_even(6) is True` cause function can be changed and `assert` also will pass, but this will be another function(another result will be returned) – chinskiy Oct 08 '18 at 10:59
  • Why would you ever return the string 'True' instead of returning `True` if what you mean is `True`? If you are using assert to check whether a function is returning the expected string then PEP8's advice about booleans isn't relevant. – T Burgis Oct 08 '18 at 11:01
  • Ok, doing `assert is_even(6) is True` and `assert is_even(5) is False` is reasonable, since you want to assure that `is_even` returns a boolean. OTOH, the function name implies it returns a boolean, and anyone calling it should be following PEP-8, and calling it like `if is_even(n):`, and not examining the actual value it returns. – PM 2Ring Oct 08 '18 at 11:08
  • @TBurgis it's was just example with `True` and True. maybe not best. but thanks for your input about PEP8's advice about booleans! – chinskiy Oct 08 '18 at 11:17
  • 2
    Also note that `assert` is often mis-used, it should only really be used for sanity checks (things that should be impossible), you should `raise` an exception for anything else like bad input data – Chris_Rands Oct 11 '18 at 20:36
  • 3
    PEP-8 is *crystal clear* on this: *Don't compare boolean values to True or False using ==.*. It doesn't matter where you do this; the examples use `if` but that doesn't matter. The assertion applies to *any context*. It doesn't matter if the test is done for `if`, or `assert`, or `while`, a conditional expression or as a stand-alone expression whose result is stored in a variable. – Martijn Pieters Oct 14 '18 at 13:26

2 Answers2

4

Code like

if a:
    return True
else:
    return False

Is unclean anyway. It should be written as return a or, if a bool (and not just a truish value) is necessary, return bool(a).

If you use v == True or v is True is completely up to the situation. The first would check if a value equals the value True. Whether a value does this, is also up to the class of this value (1 == True is true, 1.0 == True is true, but 1j == True is false, for instance).

If you compare using is, then you make it absolutely clear that you accept the value True and nothing else.

In most cases, however, I assume that just stating assert f(x) would be the semantically correct thing to do. For instance for an assert like assert is_in_rectangle(point). To spell that out as assert is_in_rectangle(point) == True or assert is_in_rectangle(point) is True would be wrong because any truish value (e. g. [ 1 ]) should suffice. Checking for an explicit True as the result would be assuming too much.

So, baseline: Don't compare truish values to True. Compare result values only to True if you have very specific demands on the value like "must be a bool, not just truish" (which is a rare case).

Alfe
  • 56,346
  • 20
  • 107
  • 159
3

assert is exactly like if. It is a keyword, followed by an boolean expression, and it requires the expression to evaluate to True. The statement assert expression will succeed if and only if the if expression succeeds.

Given that, there are two aspects to your questions. The first condition vs condition==True. This is a matter of style. You don't write if condition==True:, but simply if condition:. Similarly, there is no reason to write

assert is_even(6) == True

Just write

assert is_even(6)

instead.

The second is condition==True vs condition is True. This is not a matter of style only. Run the following

if []==False:  # also: if not []
    print('false')

if bool([]) is False:
    print('false')

if [] is False:
    print('false')

Examples (1) and (2) will print, but example (3) will not.

False is a singleton value, meaning that there is only one False object in the python interpreter, and all false objects are the same object. So, using is feels right (just like it is considered good style to write if object is None). However, there is the concept of falsy/truthy values in python: if condition does not necessarily trigger if condition is false, but if bool(condition) is false. That's done automatically. In the example above, the empty list is a falsy value. That is bool([]) evaluates to the False object (which, again, is only one). So, example (1) succeeds, because it is automatically converted (internally) to something like example (2). However, example (3) does not work, because, although an empty list evaluates to false, it is not False. It is an empty list. So, to sum up you example

if greeting: # good style, and it works
if greeting==True: # bad style, redundant, but it still works
if greeting is True: # misleading. does a different thing than it seems to do. avoid it

Closing comment: you give the example

def is_even(number):
    if number % 2 == 0:
        return <>
    else:
        return False

where you mention that <> could be any truthy value. In that spirit, it would be equivalent to write

def is_even(number):
     return number % 2

since it would return non-zero (truthy) or zero (falsy). However, don't do that. A user of a function called is_even would expect the function to return True or False, not an integer. You don't know how that user is using your code (eg. might send it to a database, or web service that expects a really boolean value). So it would be better to return a boolean value. So, the shortest and safest form of the function would be to write

def is_even(number):
    return bool(number % 2)
blue_note
  • 27,712
  • 9
  • 72
  • 90