SlideShare a Scribd company logo
Python Assertion and Unit Testing
MT_01_unittest_python.pdf
In Debug > Settings Wheel > Configurations you can review your
config and edit the launch.json file. For example add arguments.
Python – VS Code debug
Python – VS Code debug
• launch.json with custom args
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit:
// https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"cwd": "${fileDirname}",
"args": [
"--movie=flowers_google/flowers.mp4"
]
},
{
"name": "Listen for XDebug",
"type": "php",
"request": "launch",
"port": 9000
},
{
"name": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 9000
},
{
"name": "PowerShell: Launch Current File",
"type": "PowerShell",
"request": "launch",
"script": "${file}",
"cwd": "${file}"
}
]
}
Essential VS Code extensions
and keep your Python up to date
• VS Code: https://code.visualstudio.com/docs/python/python-tutorial
• To enable VS Code linting (a tool that analyzes source code to flag programming
errors, bugs, stylistic errors, suspicious constructs, etc.)

$ pip install pylint | To force a specific package version: $ pip install numpy==1.19.0
 To list currently installed packages and versions $ pip list
• To upgrade all local packages use pip-review (from elevated console)

$ pip install pip-review

$ pip-review --local --interactive
VS Code Settings (JSON)

To make various things work as it should in VS Code you may need to edit the View >
Command Palatte > Preferences: Open Settings (JSON) > settings.json file

For example to get Python to work correct with intellisense and pylint (in this case the
package cv2)

1. In VScode: CTRL + Shift + P

2. Choose "Preferences: Open Settings (JSON)"

3. Add the settings below to the settings JSON file

To make other essential adjustments search for: vscode python ”Open Settings
(JSON)” and your topic, program language etc.

More info at: https://code.visualstudio.com/docs/getstarted/tips-and-tricks
{
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.linting.pylintArgs": ["--extension-pkg-whitelist=cv2"],
...
}
Python Assert statements
• Assertions are statements that assert or
state a fact confidently in your program
• Assertions are simply boolean
expressions that checks if the
conditions return true or false
• If it is true, the program does nothing
and move to the next line of code. If it's
false, the program stops and throws an
error
• It is also a debugging tool as it brings
the program to halt as soon as any error
have occurred and shows the location
where in the program the error occurred
Python assert statement example
• The condition is supposed to be always true. If the condition is false assert halts
the program and gives an AssertionError: assert <condition>, <error_message>
def calc_factorial(num):
if isinstance(num, str):
print("Sorry, factorial does not exist for string input")
return False
elif num < 0:
print("Sorry, factorial does not exist for negative numbers")
return False
elif int(num) != num:
print("Sorry, factorial does not exist for real numbers")
return False
elif num == 0:
print("The factorial of 0 is 1")
return 1
else:
factorial = 1
for i in range(1, num + 1):
factorial = factorial * i
print("The factorial of", num ,"is", factorial)
return factorial
def checkcalc_factorial(num, expected_value, assert_error):
'''Test code below this line'''
ret_val = calc_factorial(num)
# assert <condition>, <error message>
# if condition is not satisfied (true) program will stop and throw an AssertionError
assert ret_val == expected_value, assert_error
print(f"{assert_error}: {ret_val == expected_value} ... OK")
def test_negative_numbers_return_false():
checkcalc_factorial(-1, False, "test_negative_numbers_return_false")
def test_non_integer_return_false():
checkcalc_factorial(0.5, False, "test_non_integer_return_false")
def test_when_input_is_zero_return_one():
checkcalc_factorial(0, 1, "test_when_input_is_zero_return_one")
def test_when_input_is_three_teturn_six():
checkcalc_factorial(3, 6, "test_when_input_is_three_teturn_six")
def test_string_input_return_false():
checkcalc_factorial('t', False, "test_string_input_return_false")
if __name__ == '__main__':
try:
"""
# change the value for a different result
num = 7
# uncomment to take input from the user
num = int(input("Enter a number: "))
calc_factorial(num):
"""
# test code
test_negative_numbers_return_false()
test_non_integer_return_false()
test_when_input_is_zero_return_one()
test_when_input_is_three_teturn_six()
test_string_input_return_false()
except AssertionError as ex:
print(f"AssertionError: {ex}")
assert_factorial.py
Unit Testing with Python 1
• Python have several of testing frameworks available
– One is unittest, others are doctest, Nose and pytest
• unittest is inspired from JUnit and has a similar flavor as major unit testing
frameworks in other languages
– It is organized around test case classes which contain test case methods
– Naming follows Java camelCase in contrast to Pythons snake_case

PEP 8 style guide: https://www.python.org/dev/peps/pep-0008/
• unittest supports
– test automation,
– sharing of setup and shutdown code for tests,
– aggregation of tests into collections,
– and independence of the tests from the reporting framework
• Documentation
– https://docs.python.org/3/library/unittest.html
Unit Testing with Python 2
• unittest supports some important concepts in an object-oriented way
• test fixture
– A test fixture represents the preparation needed to perform one or more tests, and any
associate cleanup actions. This may involve, for example, creating temporary or proxy
databases, directories, or starting a server process
• test case
– A test case is the individual unit of testing. It checks for a specific response to a particular
set of inputs. unittest provides a base class, unittest.TestCase, which may be used to
create new test cases
• test suite
– A test suite is a collection of test cases, test suites, or both. It is used to aggregate tests
that should be executed together
• test runner (or test driver)
– A test runner is a component which orchestrates the execution of tests and provides the
outcome to the user. The runner may use a graphical interface, a textual interface, or
return a special value to indicate the results of executing the tests
App testing (black box) vs. unit testing (white box)
Unit testing general
• Test methods recommended naming convention

(Test)_MethodToTest_ScenarioWeTest_ExpectedBehaviour

In the test method the pattern we use is ”tripple A”

Arrange, Act and Assert # Python
class RoundtripCheck(unittest.TestCase):
def test_to_roman_roundtrip_return_equal(self):
# The arrangement below is called tripple A
# Arrange - here we initialize our objects
integer = known_values[1]
# Act - here we act on the objects
numeral = to_roman(integer)
result = from_roman(numeral)
# Assert - here we verify the result
self.assertEqual(integer, result)
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'],
exit=False)
// In C# with MSTest, Nunit, xUnit
[TestClass], [TestFixture],
public class ReservationsTests
{
[TestMethod], [Test], [Fact]
public void CanBeCancelledBy_AdminCancelling_ReturnsTrue()
{
// The arrangement below is called tripple A
// Arrange - here we initialize our objects
var reservation = new Reservation();
// Act - here we act on the objects
var result = reservation.CanBeCancelledBy(
new User { IsAdmin = true });
// Assert - here we verify the result
Assert.IsTrue(result);
}
unittest most common assert methods
https://docs.python.org/3/library/unittest.html#assert-methods
• Usually all assertions also take an optional message argument
• Template: self.assert***(first_arg, second_arg, msg=None) # msg is for error info etc.
A very limited Python unittest example
import sys
# make ZeroDivisionError pass
class MyZeroDivisionError(ValueError):
pass
def fact(n):
""" Factorial function, arg n: Number
returns: factorial of n """
if n == 0:
return 1
return n * fact(n - 1)
def div(n):
""" Just divide """
if n == 0:
raise MyZeroDivisionError('division by zero!')
res = 10 / n
return res
def main(n):
print(fact(n))
print(div(n))
if __name__ == '__main__':
if len(sys.argv) > 1:
main(int(sys.argv[1]))
import unittest
import factorial
# derivation from base class is necessary -> unittest.TestCase
# to run via console: python -m unittest --v
class TestFactorial(unittest.TestCase):
test_fact_inp = (0, 1, 2, 4, 5, 6, 10,)
test_fact_out = (1, 1, 2, 24, 120, 720, 3628800,)
def test_factorial_return_equal(self):
""" Testing fact as res = fact(5)
self.assertEqual(res, 120) """
for inp, out in zip(self.test_fact_inp, self.test_fact_out):
result = factorial.fact(inp)
message = f'factorial inp: {inp} and out: {out} gives an error message!'
self.assertEqual(out, result, message)
class TestDiv(unittest.TestCase):
@classmethod
def setUpClass(cls):
print('setupClass')
@classmethod
def tearDownClass(cls):
print('nteardownClass')
def test_div_return_ZeroDivisionError(self):
""" Test exception raise due to run time error in the div function """
self.assertRaises(factorial.MyZeroDivisionError, factorial.div, 0)
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
test_factorial.py
factorial.py
Python exceptions
BaseException
Exception
ArithmeticError
FloatingPointError
OverflowError
ZeroDivisionError
AssertionError
AttributeError
BufferError
EOFError
ImportError
ModuleNotFoundError
LookupError
IndexError
KeyError
MemoryError
NameError
UnboundLocalError
OSError
BlockingIOError
ChildProcessError
ConnectionError
BrokenPipeError
ConnectionAbortedError
ConnectionRefusedError
ConnectionResetError
FileExistsError
FileNotFoundError
InterruptedError
IsADirectoryError
NotADirectoryError
PermissionError
PermissionError
ProcessLookupError
TimeoutError
ReferenceError
RuntimeError
NotImplementedError
RecursionError
StopIteration
StopAsyncIteration
SyntaxError
IndentationError
TabError
SystemError
TypeError
ValueError
UnicodeError
UnicodeDecodeError
UnicodeEncodeError
UnicodeTranslateError
Warning
BytesWarning
DeprecationWarning
FutureWarning
ImportWarning
PendingDeprecationWarning
ResourceWarning
RuntimeWarning
SyntaxWarning
UnicodeWarning
UserWarning
GeneratorExit
KeyboardInterrupt
SystemExit
Read More!
https://
julien.danjou.info/
python-exceptions-
guide/
https://airbrake.io/
blog/python-
exception-handling/
class-hierarchy
• It is also possible to check the production of exceptions, warnings, and log
messages using the following assert methods
• Template: self.assert***(exception, callable/fun, *args, **keywords)
AssertRaises etc.
https://docs.python.org/3/library/unittest.html#assert-methods
self.assertRaises(factorial.MyZeroDivisionError, factorial.div, 0)
•
• assertRises() test that a specific exception is raised when callable/fun is called with
any extra keyword arguments

The test

Passes if the specific exception is raised

Is an error if another exception is raised

Fails if no exception is raised
• AssertRises() can also return a context manager by using the with statement

Context managers are a way of allocating and releasing some sort of resource
exactly when/where you need it. Example using with and file access
assertRaises and keywords with 1
self.assertRaises(basic1.MyTypeError, basic1.my_split, 'hello world', 2)
with open('file_to_write.txt', 'w') as fh_cm:
fh_cm.write('file contents')
• assertRaises(exception, *, msg=None) using keyword with
• If a callable/fun argument is not provided assertRaises returns an optional context
manager which can be used to access exception details
• Used as a context manager, assertRaises fails if associated with body does not raise
• When used as a context manager, assertRaises() accepts the additional keyword
argument msg
• The context manager will store the caught exception object in its exception attribute
• This can be useful if the intention is to perform additional checks on the exception
raised
assertRaises with keywords with 2
# alternative call with an optional contex manager
with self.assertRaises(basic1.MyTypeError) as cm:
basic1.my_split('hello world', 2)
# (cm) that also can perform additional checks on the exception raised
self.assertEqual('Input separator must be a string', cm.exception.args[0])
• assertRaisesRegex can match a string representation of a raised exception

Template: self.assertRaisesRegex(exception, regex, callable/fun, *args, **keywords)

regex may be a regular expression object or a string - equivalent to a regex.search()
• assertWarns(warning, callable, *args, **kwds), assertWarns(warning, *,
msg=None) and assertWarnsRegex(***) works in similar ways
• assertLogs(logger=None, level=None)

A context manager to test that at least one message is logged on the logger or one
of its children, with at least the given level
AssertRaisesRegex etc.
'''split should raise error with non-string input separator and if regex does not match'''
self.assertRaisesRegex(basic1.MyTypeError, 'Input', basic1.my_split, 'hello world', 2)
# alternative call with an optional contex manager
with self.assertRaisesRegex(basic1.TypeError, 'Input') as cm:
basic1.my_split('hello world', 2)
self.assertEqual('Input', cm.expected_regex.pattern)
unittest more specific assert methods
• More specific and type specific
asserts methods – all take at least
an optional message argument
A Python unittest example 2
'''basic1.py reworked example From:
https://docs.python.org/3/library/unittest.html
To run it: python basic1.py'''
import sys
# my class definition of the TypeError exception
# Calling reserved word pass does nothing
class MyTypeError(ValueError):
pass
def my_upper(my_str):
return my_str.upper()
def my_isupper(my_str):
return my_str.isupper()
def my_split(my_str, sep):
# check if separator is a string
if not isinstance(sep, str):
raise MyTypeError('''Input separator must be a string''')
return my_str.split(sep)
try:
ss = input("Enter a string: ")
print(f"my_upper: {my_upper(ss)}")
print(f"my_isupper: {my_isupper(ss)}")
print(f"my_split: {my_split(ss, ' ')}")
print(f"my_split: {my_split(ss, 2)}")
except BaseException as ex:
print(f"TypeError: {ex}")
sys.exit(0)
import basic1, unittest
'''test_basic1.py reworked example from: https://docs.python.org/3/library/unittest.html
To run it: python -m unittest --v Here is a short script to test three string methods:'''
class TestStringMethods(unittest.TestCase):
def test_upper(self): # test case methods must start with test
self.assertEqual(basic1.my_upper('foo'), 'FOO')
def test_isupper(self):
self.assertTrue(basic1.my_isupper('FOO'))
self.assertFalse(basic1.my_isupper('Foo'))
def test_split(self):
ss = 'hello world'
self.assertEqual(basic1.my_split(ss, ' '), ['hello', 'world'])
def test_non_string_split(self):
self.assertRaises(basic1.MyTypeError, basic1.my_split, 'hello world', 2)
# alternative call with an optional contex manager
with self.assertRaises(basic1.MyTypeError) as cm:
basic1.my_split('hello world', 2)
# (cm) that also can perform additional checks on the exception raised
self.assertEqual('Input separator must be a string', cm.exception.args[0])
def test_non_string_split_regex(self):'
self.assertRaisesRegex(basic1.MyTypeError, 'Input', basic1.my_split, 'hello world', 2)
# alternative call with an optional contex manager
with self.assertRaisesRegex(basic1.MyTypeError, 'Input') as cm:
basic1.my_split('hello world', 2)
# (cm) that also can perform additional checks on the exception raised
self.assertEqual('Input', cm.expected_regex.pattern)
if __name__ == '__main__':
unittest.main(argv=['first-arg-is-ignored'], exit=False)
test_basic1.py
basic1.py
Unit Testing in practice 1
• Good unit tests
– Are fully automated, i.e. write code to test code
– Offer good coverage of the code under test, including boundary cases and error handling
paths
– Are easy to read and maintain – acts like some documentation for the source code
– Express the intent of the code under test – test cases do more than just check it
– Enables refactoring and updates of the code with confidence
• Not so good unit tests
– Monolitic tests: all test cases in a single function
• Ex. test_foo()
– Ad hoc tests: test cases are scattered across test functions
• Ex. test_1(), test_2(), ...
– Procedural tests: test cases bundled into a test method that directly correspond to a target
method (as in the basic1 code example with isupper())
• Ex. test_foo() → (is testing the function) foo()
Unit Testing in practice 2
• A test is not a unit test if ...
– It talks to the database
– It communicates across the network
– It touches the file system
– It cannot run at the same time as any of your other unit tests
– You have to do special things to your environment (such as editing config files)
to run it
– https://www.artima.com/weblogs/viewpost.jsp?thread=126923
• Tests that do these things aren't bad. Often they are worth writing, and they
can be written in a unit test harness (as part of a specially prepared test
environment needed to execute the test)
• However, it is important to be able to separate them from true unit tests so
that we can keep a set of tests that we can run fast whenever we make our
changes
Code coverage
• Coverage measurement is typically used to gauge the effectiveness of tests.
It can show which parts of your code are being executed (covered) by the
test suite, and which are not
• Function coverage – Has each function (or subroutine) in the program been called?
• Statement coverage – Has each statement in the program been executed?
• Edge coverage – has every edge (arrow) in the Control Flow Graph been executed?

Branch coverage – Has each branch (also called Decision-to-Decision path) of
each control structure (such as in if and case statements) been executed? For
example, given an if statement, have both the true and false branches been
executed? Notice that this is a subset of Edge coverage
• Condition coverage – Has each Boolean sub-expression
evaluated both to true and false?
https://en.wikipedia.org/wiki/Code_coverage
https://en.wikipedia.org/wiki/Control-flow_graph
Coverage.py
• Coverage.py is a tool for measuring code
coverage of Python programs. It monitors your
program, noting which parts of the code have been
executed, then analyzes the source to identify
code that could have been executed but was not
• Note that high coverage numbers does not mean
that your code is clean from bugs!
# install
$ pip install coverage
$ coverage help
usage: coverage <command> [options] [args]
# run your test code (.coverage is created)
$ coverage run --branch test_factorial.py
# report to console (from .coverage file)
$ coverage report -m
# report to html (htmlcov folder is created)
$ coverage html
PS C:python_unittesting> coverage report -m
Name Stmts Miss Branch BrPart Cover Missing
---------------------------------------------------------------
factorial.py 19 8 8 2 56% 29-30, 33-36, 39-40, 27->29, 38->39
test_factorial.py 14 0 4 1 94% 23->exit
---------------------------------------------------------------
TOTAL 33 8 12 3 71%
Recommended viewing and reading
• Python Tutorial: Unit Testing Your Code with the unittest Module

https://www.youtube.com/watch?v=6tNS--WetLI
• Dive into Python 3

https://github.com/diveintomark/diveintopython3
• Python 3.x unittest documentation

https://docs.python.org/3/library/unittest.html
• Code Coverage

https://coverage.readthedocs.io

https://en.wikipedia.org/wiki/Code_coverage
• Martin Fowler on TestCoverage

https://martinfowler.com/bliki/TestCoverage.html

More Related Content

PPT
Test Driven Development with PHPUnit
PDF
Тестирование и Django
PDF
We Are All Testers Now: The Testing Pyramid and Front-End Development
PDF
Php tests tips
KEY
How To Test Everything
PPT
Automated Unit Testing
PDF
Test driven development
PDF
33rd Degree 2013, Bad Tests, Good Tests
Test Driven Development with PHPUnit
Тестирование и Django
We Are All Testers Now: The Testing Pyramid and Front-End Development
Php tests tips
How To Test Everything
Automated Unit Testing
Test driven development
33rd Degree 2013, Bad Tests, Good Tests

Similar to MT_01_unittest_python.pdf (20)

KEY
Django’s nasal passage
KEY
Php Unit With Zend Framework Zendcon09
PDF
Object Oriented PHP - PART-2
PPT
Unit Testing using PHPUnit
PDF
Testing Code and Assuring Quality
PPTX
Introduction to nsubstitute
PPT
Phpunit
PPT
Java Tut1
PPT
Java Tutorial
PPT
Java tut1
PPT
Tutorial java
PDF
maXbox Starter 36 Software Testing
PDF
Effective testing with pytest
PPTX
Full Stack Unit Testing
PPTX
Test in action week 2
PDF
Introduction to Unit Testing with PHPUnit
PDF
Unit testing for WordPress
PDF
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
PDF
Isolated development in python
PDF
Leveling Up With Unit Testing - php[tek] 2023
Django’s nasal passage
Php Unit With Zend Framework Zendcon09
Object Oriented PHP - PART-2
Unit Testing using PHPUnit
Testing Code and Assuring Quality
Introduction to nsubstitute
Phpunit
Java Tut1
Java Tutorial
Java tut1
Tutorial java
maXbox Starter 36 Software Testing
Effective testing with pytest
Full Stack Unit Testing
Test in action week 2
Introduction to Unit Testing with PHPUnit
Unit testing for WordPress
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Isolated development in python
Leveling Up With Unit Testing - php[tek] 2023
Ad

Recently uploaded (20)

PDF
A GUIDE TO GENETICS FOR UNDERGRADUATE MEDICAL STUDENTS
PDF
RMMM.pdf make it easy to upload and study
PPTX
Lesson notes of climatology university.
PDF
Trump Administration's workforce development strategy
PDF
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
PDF
VCE English Exam - Section C Student Revision Booklet
PPTX
Pharma ospi slides which help in ospi learning
PPTX
GDM (1) (1).pptx small presentation for students
PPTX
1st Inaugural Professorial Lecture held on 19th February 2020 (Governance and...
PDF
FourierSeries-QuestionsWithAnswers(Part-A).pdf
PPTX
Introduction-to-Literarature-and-Literary-Studies-week-Prelim-coverage.pptx
PPTX
Microbial diseases, their pathogenesis and prophylaxis
PDF
Chapter 2 Heredity, Prenatal Development, and Birth.pdf
PDF
Complications of Minimal Access Surgery at WLH
PDF
Black Hat USA 2025 - Micro ICS Summit - ICS/OT Threat Landscape
PPTX
Tissue processing ( HISTOPATHOLOGICAL TECHNIQUE
PDF
A systematic review of self-coping strategies used by university students to ...
PPTX
human mycosis Human fungal infections are called human mycosis..pptx
PDF
Weekly quiz Compilation Jan -July 25.pdf
PDF
STATICS OF THE RIGID BODIES Hibbelers.pdf
A GUIDE TO GENETICS FOR UNDERGRADUATE MEDICAL STUDENTS
RMMM.pdf make it easy to upload and study
Lesson notes of climatology university.
Trump Administration's workforce development strategy
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
VCE English Exam - Section C Student Revision Booklet
Pharma ospi slides which help in ospi learning
GDM (1) (1).pptx small presentation for students
1st Inaugural Professorial Lecture held on 19th February 2020 (Governance and...
FourierSeries-QuestionsWithAnswers(Part-A).pdf
Introduction-to-Literarature-and-Literary-Studies-week-Prelim-coverage.pptx
Microbial diseases, their pathogenesis and prophylaxis
Chapter 2 Heredity, Prenatal Development, and Birth.pdf
Complications of Minimal Access Surgery at WLH
Black Hat USA 2025 - Micro ICS Summit - ICS/OT Threat Landscape
Tissue processing ( HISTOPATHOLOGICAL TECHNIQUE
A systematic review of self-coping strategies used by university students to ...
human mycosis Human fungal infections are called human mycosis..pptx
Weekly quiz Compilation Jan -July 25.pdf
STATICS OF THE RIGID BODIES Hibbelers.pdf
Ad

MT_01_unittest_python.pdf

  • 1. Python Assertion and Unit Testing
  • 3. In Debug > Settings Wheel > Configurations you can review your config and edit the launch.json file. For example add arguments. Python – VS Code debug
  • 4. Python – VS Code debug • launch.json with custom args { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: // https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Python: Current File", "type": "python", "request": "launch", "program": "${file}", "console": "integratedTerminal", "cwd": "${fileDirname}", "args": [ "--movie=flowers_google/flowers.mp4" ] }, { "name": "Listen for XDebug", "type": "php", "request": "launch", "port": 9000 }, { "name": "Launch currently open script", "type": "php", "request": "launch", "program": "${file}", "cwd": "${fileDirname}", "port": 9000 }, { "name": "PowerShell: Launch Current File", "type": "PowerShell", "request": "launch", "script": "${file}", "cwd": "${file}" } ] }
  • 5. Essential VS Code extensions and keep your Python up to date • VS Code: https://code.visualstudio.com/docs/python/python-tutorial • To enable VS Code linting (a tool that analyzes source code to flag programming errors, bugs, stylistic errors, suspicious constructs, etc.)  $ pip install pylint | To force a specific package version: $ pip install numpy==1.19.0  To list currently installed packages and versions $ pip list • To upgrade all local packages use pip-review (from elevated console)  $ pip install pip-review  $ pip-review --local --interactive
  • 6. VS Code Settings (JSON)  To make various things work as it should in VS Code you may need to edit the View > Command Palatte > Preferences: Open Settings (JSON) > settings.json file  For example to get Python to work correct with intellisense and pylint (in this case the package cv2)  1. In VScode: CTRL + Shift + P  2. Choose "Preferences: Open Settings (JSON)"  3. Add the settings below to the settings JSON file  To make other essential adjustments search for: vscode python ”Open Settings (JSON)” and your topic, program language etc.  More info at: https://code.visualstudio.com/docs/getstarted/tips-and-tricks { "python.linting.pylintEnabled": true, "python.linting.enabled": true, "python.linting.pylintArgs": ["--extension-pkg-whitelist=cv2"], ... }
  • 7. Python Assert statements • Assertions are statements that assert or state a fact confidently in your program • Assertions are simply boolean expressions that checks if the conditions return true or false • If it is true, the program does nothing and move to the next line of code. If it's false, the program stops and throws an error • It is also a debugging tool as it brings the program to halt as soon as any error have occurred and shows the location where in the program the error occurred
  • 8. Python assert statement example • The condition is supposed to be always true. If the condition is false assert halts the program and gives an AssertionError: assert <condition>, <error_message> def calc_factorial(num): if isinstance(num, str): print("Sorry, factorial does not exist for string input") return False elif num < 0: print("Sorry, factorial does not exist for negative numbers") return False elif int(num) != num: print("Sorry, factorial does not exist for real numbers") return False elif num == 0: print("The factorial of 0 is 1") return 1 else: factorial = 1 for i in range(1, num + 1): factorial = factorial * i print("The factorial of", num ,"is", factorial) return factorial def checkcalc_factorial(num, expected_value, assert_error): '''Test code below this line''' ret_val = calc_factorial(num) # assert <condition>, <error message> # if condition is not satisfied (true) program will stop and throw an AssertionError assert ret_val == expected_value, assert_error print(f"{assert_error}: {ret_val == expected_value} ... OK") def test_negative_numbers_return_false(): checkcalc_factorial(-1, False, "test_negative_numbers_return_false") def test_non_integer_return_false(): checkcalc_factorial(0.5, False, "test_non_integer_return_false") def test_when_input_is_zero_return_one(): checkcalc_factorial(0, 1, "test_when_input_is_zero_return_one") def test_when_input_is_three_teturn_six(): checkcalc_factorial(3, 6, "test_when_input_is_three_teturn_six") def test_string_input_return_false(): checkcalc_factorial('t', False, "test_string_input_return_false") if __name__ == '__main__': try: """ # change the value for a different result num = 7 # uncomment to take input from the user num = int(input("Enter a number: ")) calc_factorial(num): """ # test code test_negative_numbers_return_false() test_non_integer_return_false() test_when_input_is_zero_return_one() test_when_input_is_three_teturn_six() test_string_input_return_false() except AssertionError as ex: print(f"AssertionError: {ex}") assert_factorial.py
  • 9. Unit Testing with Python 1 • Python have several of testing frameworks available – One is unittest, others are doctest, Nose and pytest • unittest is inspired from JUnit and has a similar flavor as major unit testing frameworks in other languages – It is organized around test case classes which contain test case methods – Naming follows Java camelCase in contrast to Pythons snake_case  PEP 8 style guide: https://www.python.org/dev/peps/pep-0008/ • unittest supports – test automation, – sharing of setup and shutdown code for tests, – aggregation of tests into collections, – and independence of the tests from the reporting framework • Documentation – https://docs.python.org/3/library/unittest.html
  • 10. Unit Testing with Python 2 • unittest supports some important concepts in an object-oriented way • test fixture – A test fixture represents the preparation needed to perform one or more tests, and any associate cleanup actions. This may involve, for example, creating temporary or proxy databases, directories, or starting a server process • test case – A test case is the individual unit of testing. It checks for a specific response to a particular set of inputs. unittest provides a base class, unittest.TestCase, which may be used to create new test cases • test suite – A test suite is a collection of test cases, test suites, or both. It is used to aggregate tests that should be executed together • test runner (or test driver) – A test runner is a component which orchestrates the execution of tests and provides the outcome to the user. The runner may use a graphical interface, a textual interface, or return a special value to indicate the results of executing the tests
  • 11. App testing (black box) vs. unit testing (white box)
  • 12. Unit testing general • Test methods recommended naming convention  (Test)_MethodToTest_ScenarioWeTest_ExpectedBehaviour  In the test method the pattern we use is ”tripple A”  Arrange, Act and Assert # Python class RoundtripCheck(unittest.TestCase): def test_to_roman_roundtrip_return_equal(self): # The arrangement below is called tripple A # Arrange - here we initialize our objects integer = known_values[1] # Act - here we act on the objects numeral = to_roman(integer) result = from_roman(numeral) # Assert - here we verify the result self.assertEqual(integer, result) if __name__ == '__main__': unittest.main(argv=['first-arg-is-ignored'], exit=False) // In C# with MSTest, Nunit, xUnit [TestClass], [TestFixture], public class ReservationsTests { [TestMethod], [Test], [Fact] public void CanBeCancelledBy_AdminCancelling_ReturnsTrue() { // The arrangement below is called tripple A // Arrange - here we initialize our objects var reservation = new Reservation(); // Act - here we act on the objects var result = reservation.CanBeCancelledBy( new User { IsAdmin = true }); // Assert - here we verify the result Assert.IsTrue(result); }
  • 13. unittest most common assert methods https://docs.python.org/3/library/unittest.html#assert-methods • Usually all assertions also take an optional message argument • Template: self.assert***(first_arg, second_arg, msg=None) # msg is for error info etc.
  • 14. A very limited Python unittest example import sys # make ZeroDivisionError pass class MyZeroDivisionError(ValueError): pass def fact(n): """ Factorial function, arg n: Number returns: factorial of n """ if n == 0: return 1 return n * fact(n - 1) def div(n): """ Just divide """ if n == 0: raise MyZeroDivisionError('division by zero!') res = 10 / n return res def main(n): print(fact(n)) print(div(n)) if __name__ == '__main__': if len(sys.argv) > 1: main(int(sys.argv[1])) import unittest import factorial # derivation from base class is necessary -> unittest.TestCase # to run via console: python -m unittest --v class TestFactorial(unittest.TestCase): test_fact_inp = (0, 1, 2, 4, 5, 6, 10,) test_fact_out = (1, 1, 2, 24, 120, 720, 3628800,) def test_factorial_return_equal(self): """ Testing fact as res = fact(5) self.assertEqual(res, 120) """ for inp, out in zip(self.test_fact_inp, self.test_fact_out): result = factorial.fact(inp) message = f'factorial inp: {inp} and out: {out} gives an error message!' self.assertEqual(out, result, message) class TestDiv(unittest.TestCase): @classmethod def setUpClass(cls): print('setupClass') @classmethod def tearDownClass(cls): print('nteardownClass') def test_div_return_ZeroDivisionError(self): """ Test exception raise due to run time error in the div function """ self.assertRaises(factorial.MyZeroDivisionError, factorial.div, 0) if __name__ == '__main__': unittest.main(argv=['first-arg-is-ignored'], exit=False) test_factorial.py factorial.py
  • 15. Python exceptions BaseException Exception ArithmeticError FloatingPointError OverflowError ZeroDivisionError AssertionError AttributeError BufferError EOFError ImportError ModuleNotFoundError LookupError IndexError KeyError MemoryError NameError UnboundLocalError OSError BlockingIOError ChildProcessError ConnectionError BrokenPipeError ConnectionAbortedError ConnectionRefusedError ConnectionResetError FileExistsError FileNotFoundError InterruptedError IsADirectoryError NotADirectoryError PermissionError PermissionError ProcessLookupError TimeoutError ReferenceError RuntimeError NotImplementedError RecursionError StopIteration StopAsyncIteration SyntaxError IndentationError TabError SystemError TypeError ValueError UnicodeError UnicodeDecodeError UnicodeEncodeError UnicodeTranslateError Warning BytesWarning DeprecationWarning FutureWarning ImportWarning PendingDeprecationWarning ResourceWarning RuntimeWarning SyntaxWarning UnicodeWarning UserWarning GeneratorExit KeyboardInterrupt SystemExit Read More! https:// julien.danjou.info/ python-exceptions- guide/ https://airbrake.io/ blog/python- exception-handling/ class-hierarchy
  • 16. • It is also possible to check the production of exceptions, warnings, and log messages using the following assert methods • Template: self.assert***(exception, callable/fun, *args, **keywords) AssertRaises etc. https://docs.python.org/3/library/unittest.html#assert-methods self.assertRaises(factorial.MyZeroDivisionError, factorial.div, 0)
  • 17. • • assertRises() test that a specific exception is raised when callable/fun is called with any extra keyword arguments  The test  Passes if the specific exception is raised  Is an error if another exception is raised  Fails if no exception is raised • AssertRises() can also return a context manager by using the with statement  Context managers are a way of allocating and releasing some sort of resource exactly when/where you need it. Example using with and file access assertRaises and keywords with 1 self.assertRaises(basic1.MyTypeError, basic1.my_split, 'hello world', 2) with open('file_to_write.txt', 'w') as fh_cm: fh_cm.write('file contents')
  • 18. • assertRaises(exception, *, msg=None) using keyword with • If a callable/fun argument is not provided assertRaises returns an optional context manager which can be used to access exception details • Used as a context manager, assertRaises fails if associated with body does not raise • When used as a context manager, assertRaises() accepts the additional keyword argument msg • The context manager will store the caught exception object in its exception attribute • This can be useful if the intention is to perform additional checks on the exception raised assertRaises with keywords with 2 # alternative call with an optional contex manager with self.assertRaises(basic1.MyTypeError) as cm: basic1.my_split('hello world', 2) # (cm) that also can perform additional checks on the exception raised self.assertEqual('Input separator must be a string', cm.exception.args[0])
  • 19. • assertRaisesRegex can match a string representation of a raised exception  Template: self.assertRaisesRegex(exception, regex, callable/fun, *args, **keywords)  regex may be a regular expression object or a string - equivalent to a regex.search() • assertWarns(warning, callable, *args, **kwds), assertWarns(warning, *, msg=None) and assertWarnsRegex(***) works in similar ways • assertLogs(logger=None, level=None)  A context manager to test that at least one message is logged on the logger or one of its children, with at least the given level AssertRaisesRegex etc. '''split should raise error with non-string input separator and if regex does not match''' self.assertRaisesRegex(basic1.MyTypeError, 'Input', basic1.my_split, 'hello world', 2) # alternative call with an optional contex manager with self.assertRaisesRegex(basic1.TypeError, 'Input') as cm: basic1.my_split('hello world', 2) self.assertEqual('Input', cm.expected_regex.pattern)
  • 20. unittest more specific assert methods • More specific and type specific asserts methods – all take at least an optional message argument
  • 21. A Python unittest example 2 '''basic1.py reworked example From: https://docs.python.org/3/library/unittest.html To run it: python basic1.py''' import sys # my class definition of the TypeError exception # Calling reserved word pass does nothing class MyTypeError(ValueError): pass def my_upper(my_str): return my_str.upper() def my_isupper(my_str): return my_str.isupper() def my_split(my_str, sep): # check if separator is a string if not isinstance(sep, str): raise MyTypeError('''Input separator must be a string''') return my_str.split(sep) try: ss = input("Enter a string: ") print(f"my_upper: {my_upper(ss)}") print(f"my_isupper: {my_isupper(ss)}") print(f"my_split: {my_split(ss, ' ')}") print(f"my_split: {my_split(ss, 2)}") except BaseException as ex: print(f"TypeError: {ex}") sys.exit(0) import basic1, unittest '''test_basic1.py reworked example from: https://docs.python.org/3/library/unittest.html To run it: python -m unittest --v Here is a short script to test three string methods:''' class TestStringMethods(unittest.TestCase): def test_upper(self): # test case methods must start with test self.assertEqual(basic1.my_upper('foo'), 'FOO') def test_isupper(self): self.assertTrue(basic1.my_isupper('FOO')) self.assertFalse(basic1.my_isupper('Foo')) def test_split(self): ss = 'hello world' self.assertEqual(basic1.my_split(ss, ' '), ['hello', 'world']) def test_non_string_split(self): self.assertRaises(basic1.MyTypeError, basic1.my_split, 'hello world', 2) # alternative call with an optional contex manager with self.assertRaises(basic1.MyTypeError) as cm: basic1.my_split('hello world', 2) # (cm) that also can perform additional checks on the exception raised self.assertEqual('Input separator must be a string', cm.exception.args[0]) def test_non_string_split_regex(self):' self.assertRaisesRegex(basic1.MyTypeError, 'Input', basic1.my_split, 'hello world', 2) # alternative call with an optional contex manager with self.assertRaisesRegex(basic1.MyTypeError, 'Input') as cm: basic1.my_split('hello world', 2) # (cm) that also can perform additional checks on the exception raised self.assertEqual('Input', cm.expected_regex.pattern) if __name__ == '__main__': unittest.main(argv=['first-arg-is-ignored'], exit=False) test_basic1.py basic1.py
  • 22. Unit Testing in practice 1 • Good unit tests – Are fully automated, i.e. write code to test code – Offer good coverage of the code under test, including boundary cases and error handling paths – Are easy to read and maintain – acts like some documentation for the source code – Express the intent of the code under test – test cases do more than just check it – Enables refactoring and updates of the code with confidence • Not so good unit tests – Monolitic tests: all test cases in a single function • Ex. test_foo() – Ad hoc tests: test cases are scattered across test functions • Ex. test_1(), test_2(), ... – Procedural tests: test cases bundled into a test method that directly correspond to a target method (as in the basic1 code example with isupper()) • Ex. test_foo() → (is testing the function) foo()
  • 23. Unit Testing in practice 2 • A test is not a unit test if ... – It talks to the database – It communicates across the network – It touches the file system – It cannot run at the same time as any of your other unit tests – You have to do special things to your environment (such as editing config files) to run it – https://www.artima.com/weblogs/viewpost.jsp?thread=126923 • Tests that do these things aren't bad. Often they are worth writing, and they can be written in a unit test harness (as part of a specially prepared test environment needed to execute the test) • However, it is important to be able to separate them from true unit tests so that we can keep a set of tests that we can run fast whenever we make our changes
  • 24. Code coverage • Coverage measurement is typically used to gauge the effectiveness of tests. It can show which parts of your code are being executed (covered) by the test suite, and which are not • Function coverage – Has each function (or subroutine) in the program been called? • Statement coverage – Has each statement in the program been executed? • Edge coverage – has every edge (arrow) in the Control Flow Graph been executed?  Branch coverage – Has each branch (also called Decision-to-Decision path) of each control structure (such as in if and case statements) been executed? For example, given an if statement, have both the true and false branches been executed? Notice that this is a subset of Edge coverage • Condition coverage – Has each Boolean sub-expression evaluated both to true and false? https://en.wikipedia.org/wiki/Code_coverage https://en.wikipedia.org/wiki/Control-flow_graph
  • 25. Coverage.py • Coverage.py is a tool for measuring code coverage of Python programs. It monitors your program, noting which parts of the code have been executed, then analyzes the source to identify code that could have been executed but was not • Note that high coverage numbers does not mean that your code is clean from bugs! # install $ pip install coverage $ coverage help usage: coverage <command> [options] [args] # run your test code (.coverage is created) $ coverage run --branch test_factorial.py # report to console (from .coverage file) $ coverage report -m # report to html (htmlcov folder is created) $ coverage html PS C:python_unittesting> coverage report -m Name Stmts Miss Branch BrPart Cover Missing --------------------------------------------------------------- factorial.py 19 8 8 2 56% 29-30, 33-36, 39-40, 27->29, 38->39 test_factorial.py 14 0 4 1 94% 23->exit --------------------------------------------------------------- TOTAL 33 8 12 3 71%
  • 26. Recommended viewing and reading • Python Tutorial: Unit Testing Your Code with the unittest Module  https://www.youtube.com/watch?v=6tNS--WetLI • Dive into Python 3  https://github.com/diveintomark/diveintopython3 • Python 3.x unittest documentation  https://docs.python.org/3/library/unittest.html • Code Coverage  https://coverage.readthedocs.io  https://en.wikipedia.org/wiki/Code_coverage • Martin Fowler on TestCoverage  https://martinfowler.com/bliki/TestCoverage.html