Testing
Testing
Albert-Ludwigs-Universität Freiburg
14 Oct 2019
Plan
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
NO:
Run a program on some nice examples.
NO:
Run a program on some nice examples.
YES:
Run a program with the intent of finding an error.
identify corner cases
devise tricky examples
exercise the program logic
NO:
Run a program on some nice examples.
YES:
Run a program with the intent of finding an error.
identify corner cases
devise tricky examples
exercise the program logic
Increase Confidence
Early and quick feedback on changes
Up to 20% of bugfixes introduce new bugs. Beware!
Debugging aid
TDD (test driven design)
Specification by way of test cases
Implementation proceeds along the test cases
Unit Test
Tests a unit of code in isolation.
A unit can be a single function or method, an entire class, or an entire module.
Lightweight and fast.
Unit Test
Tests a unit of code in isolation.
A unit can be a single function or method, an entire class, or an entire module.
Lightweight and fast.
Integration Test
Unit Test
Tests a unit of code in isolation.
A unit can be a single function or method, an entire class, or an entire module.
Lightweight and fast.
Integration Test
System Test
Why automatize?
Well-understood methodology
Supports TDD
Tool support
Easily automatized
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
Task
1 The function list_filter has two parameters, an integer x and a list of
integers xs, and returns the list of all elements of xs which are less than or
equal to x.
2 Write meaningful tests for this function.
Task
1 The function list_filter has two parameters, an integer x and a list of
integers xs, and returns the list of all elements of xs which are less than or
equal to x.
2 Write meaningful tests for this function.
The function list_filter has two parameters, an integer x and a list of integers
xs, and returns the list of all elements of xs which are less than or equal to x.
The function list_filter has two parameters, an integer x and a list of integers
xs, and returns the list of all elements of xs which are less than or equal to x.
The function list_filter has two parameters, an integer x and a list of integers
xs, and returns the list of all elements of xs which are less than or equal to x.
The function list_filter has two parameters, an integer x and a list of integers
xs, and returns the list of all elements of xs which are less than or equal to x.
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
pytest
(http://pytest.org/en/latest/) is a
Python tool for testing
We start with the simplest way of
using it.
A pytest Test
def test_empty():
assert list_filter (4, []) == []
def test_sharp1():
assert list_filter (4, [4]) == [4]
def test_sharp2():
assert list_filter (4, [3]) == [3]
def test_sharp3():
assert list_filter (4, [5]) == []
def test_uniform1():
assert list_filter (4, [1,3,5]) == [1,3]
def test_uniform2():
assert list_filter (4, [1,5,4]) == [1,4]
def test_sharp1():
> assert list_filter (4, [4]) == [4]
E assert [] == [4]
E Right contains more items, first extra item: 4
E Use -v to get the full diff
list_filter.py:12: AssertionError
________________________________ test_uniform2 _________________________________
def test_uniform2():
> assert list_filter (4, [1,5,4]) == [1,4]
E assert [1] == [1, 4]
E Right contains more items, first extra item: 4
E Use -v to get the full diff
list_filter.py:24: AssertionError
====================== 2 failed, 4 passed in 0.08 seconds ======================
def test_sharp1():
> assert list_filter (4, [4]) == [4]
E assert [] == [4]
E Right contains more items, first extra item: 4
E Full diff:
E - []
E + [4]
E ? +
list_filter.py:12: AssertionError
________________________________ test_uniform2 _________________________________
def test_uniform2():
> assert list_filter (4, [1,5,4]) == [1,4]
E assert [1] == [1, 4]
E Right contains more items, first extra item: 4
E Full diff:
E - [1]
E + [1, 4]
list_filter.py:24: AssertionError
Thiemann Testing Python 14 Oct 2019 18 / 67
Usability
Advice
Each test function should contain one assert to test one property!
⇒ Testing stops at the first failing assert in a function, the remaining asserts are
ignored!
The function list_filter has two parameters, an integer x and a list of integers
xs, and returns the list of all elements of xs which are less than or equal to x.
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
Task
1 The function vector_rotate has two parameters, a 2D point p and an angle
a in degrees, and returns a 2D point rotated by a degrees around the origin.
2 We represent a 2D point by a tuple.
3 Write meaningful tests for this function.
def test_rot90():
> assert vector_rotate ((1,0), 90) == (0,1)
E assert (6.123233995736766e-17, 1.0) == (0, 1)
E At index 0 diff: 6.123233995736766e-17 != 0
E Full diff:
E - (6.123233995736766e-17, 1.0)
E + (0, 1)
tuple_rotate.py:26: AssertionError
====================== 1 failed, 4 passed in 0.07 seconds ======================
Golden Rule
Never, never, never compare floating point numbers for equality!
See https://docs.python.org/3/tutorial/floatingpoint.html for the reason
Use pytest.approx
This function applies to numbers, sequences, dictionaries, numpy, etc
It modifies the comparision to make it approximate
...
rotating the unit vector (1,0) by 90 (180, 270) degrees should yield the unit
vector (0,1) (resp (-1,0), (0,-1)):
assert vector_rotate ((1,0), 90) == approx((0,1))
[actually, this modification should be applied to all tests for this function]
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
Task
Write meaningful tests for the following functions:
1 The function leap_year has one parameter, an integer y representing a year
in the Gregorian calendar, and returns whether y is a leap year or not.
The Gregorian calendar is defined for years y greater than 1582 and considers y
a leap year iff
y is divisible by 4; and
if y is divisible by 100, then y must be divisible by 400.
2 The function intersect has four 2D-points as parameters, representing two
lines in two-point-form, and returns the intersection point of those lines, if it
exists uniquely, and None otherwise.
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
On-Premise
Roll your own CI-server on a machine controlled by your institution
Preferred for closed source projects
On-Premise
Roll your own CI-server on a machine controlled by your institution
Preferred for closed source projects
On-Premise
Roll your own CI-server on a machine controlled by your institution
Preferred for closed source projects
In both cases. . .
need to run potentially faulty software in a controlled way
several simultaneous runs must be supported
⇒ some isolation mechanism should be used
industry standard: container-based approach (e.g., docker)
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
1 FROM jenkins/jenkins:lts
2 USER root
3 RUN apt-get update \
4 && apt-get install -y python3-pip python3 \
5 && rm -rf /var/lib/apt/lists/* \
6 && pip3 install -U pytest tox
7 USER jenkins
66e82ef484a04725bd0eea067e75e778
Point your browser to http://127.0.0.1:8080/ to access the Jenkins
configuration (you will be asked to the above password)
(Standard packages are more than sufficient)
Create a user and login
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
- run:
name: install dependencies
command: |
python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt
pip install -U pytest
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
import requests
def most_common_word_in_web_page(words, url):
"""
finds the most common word from a list of words in a web page, i
"""
response = requests.get(url)
text = response.text
word_frequency = {w: text.count(w) for w in words}
return sorted(words, key=word_frequency.get)[-1]
if __name__ == ’__main__’:
most_common = most_common_word_in_web_page(
[’python’, ’Python’, ’programming’],
’https://python.org/’,
)
print(most_common)
At the time of writing, this program prints Python, but who knows what
happens tomorrow?
A testing environment (in particular on a CI-Server) may not support network
connections.
There are several approaches to testing such examples
1 Modularity: separate program logic from resource access
Advantage: always a good idea
Disadvantage: the actual resource access is never tested
2 Abstraction and mocking: abstract over the resource and supply a fake resource
during testing
Advantage: can test entire code
Disadvantage: mocking must accurately mimic the resource’s behavior
3 Patching: overwrite functionality of the resource during testing
import requests
def most_common_word_in_web_page(words, url):
response = requests.get(url)
return most_common_words (words, response.text)
if __name__ == ’__main__’:
most_common = most_common_word_in_web_page(
[’python’, ’Python’, ’programming’],
’https://python.org/’,
)
print(most_common)
def test_with_mock_objects():
from unittest.mock import Mock
mock_requests = Mock()
mock_requests.get.return_value.text = ’aa bbb c’
result = most_common_word_in_web_page(
[’a’, ’b’, ’c’],
’https://python.org/’,
useragent=mock_requests
)
assert result == ’b’
assert mock_requests.get.call_count == 1
assert mock_requests.get.call_args[0][0] == ’https://python.org/
def test_mock():
mock = Mock()
mock.x = 3
mock.y = 4
mock.distance.return_value = 5
assert mock.x * mock.x + mock.y * mock.y == \
mock.distance() * mock.distance()
assert mock.distance.call_count == 2
mock.distance.assert_called_with()
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End
README.md
LICENSE
setup.py
requirements.txt
sample/__init__.py
sample/core.py
sample/helpers.py
docs/conf.py
docs/index.md
conftest.py
tests/test_basic.py
tests/test_advanced.py
See https://github.com/kennethreitz/samplemod
Thiemann Testing Python 14 Oct 2019 65 / 67
Structure of a Python Application
1 Testing Python
Let’s Test
pytest
Testing with Numbers
Exercise
2 Continuous Testing
On-Premise Server
Software as a Service
3 More Testing Secrets
Testing the Bad Case
Depending on External Resources
Structuring Test Suites
4 The End