A common practice is to end any python module with a block of instructions conditioned by __name__ == '__main__'
, containing the module unitary tests.
%%writefile mypowers.py
"""My own set of power() and powers() functions."""
def powers(x:float, ps:list) -> list:
"""Raise a value to a collection of powers."""
result = []
for p in ps:
result.append(x**p)
return result
if __name__ == '__main__':
res = powers(2, [2, 3, 4])
print(res)
if res!=[4, 8, 16]:
print('PROBLEM !')
else:
print('OK')
Overwriting mypowers.py
If the file is executed as a main program, the condition __name__ == '__main__'
will be true and the tests will be executed.
!python3 mypowers.py
[4, 8, 16] OK
If the file is imported as a module, the variable __name__
will contains the name of the module, and the tests will not be executed.
import mypowers
print('Name:', mypowers.__name__)
print(mypowers.powers(2, [2, 3, 4]))
Name: mypowers [4, 8, 16]
When the module becomes increasingly long and complex, it may becomes very tedious to setup all the tests and check all the expected results one by one.
In any docstring, one can insert some python lines, preceded by >>>
. After each such line, the block of non empty lines is the expected output.
%%writefile mypowers.py
"""
My own set of power() and powers() functions.
>>> powers(2, [2, 3, 4])
[4,8,16]
"""
def powers(x:float, ps:list) -> list:
"""Raise a value to a collection of powers."""
result = []
for p in ps:
result.append(x**p)
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
Overwriting mypowers.py
As usual, there is a final block conditioned by __name__ == "__main__"
, and one has to execute the file as a main program, so to trigger the tests.
!python3 mypowers.py
********************************************************************** File "/builds/lalens/PyPlot/doc/source/_slides/notebooks/mypowers.py", line 3, in __main__ Failed example: powers(2, [2, 3, 4]) Expected: [4,8,16] Got: [4, 8, 16] ********************************************************************** 1 items had failures: 1 of 1 in __main__ ***Test Failed*** 1 failures.
As you noticed, doctest blindly compares the actual and expected output, character after character. Let's correct.
%%writefile mypowers.py
"""
My own set of power() and powers() functions.
>>> powers(2, [2, 3, 4])
[4, 8, 16]
"""
def powers(x:float, ps:list) -> list:
"""Raise a value to a collection of powers."""
result = []
for p in ps:
result.append(x**p)
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
Overwriting mypowers.py
!python3 mypowers.py
If nothing happens, this simply means that all the tests run OK. If you want to check the details, use the -v
option.
!python3 mypowers.py -v
Trying: powers(2, [2, 3, 4]) Expecting: [4, 8, 16] ok 1 items had no tests: __main__.powers 1 items passed all tests: 1 tests in __main__ 1 tests in 2 items. 1 passed and 0 failed. Test passed.
Well, doctest is right : our test is not about the whole file, but precisely for the powers()
function. Let's move the test.
%%writefile mypowers.py
"""My own set of power() and powers() functions."""
def powers(x:float, ps:list) -> list:
"""
Raise a value to a collection of powers.
>>> powers(2, [2, 3, 4])
[4, 8, 16]
"""
result = []
for p in ps:
result.append(x**p)
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
Overwriting mypowers.py
!python3 mypowers.py -v
Trying: powers(2, [2, 3, 4]) Expecting: [4, 8, 16] ok 1 items had no tests: __main__ 1 items passed all tests: 1 tests in __main__.powers 1 tests in 2 items. 1 passed and 0 failed. Test passed.