class Doubler:
def __init__(self, n):
self._n = 2 * n
def n(self):
return self._n
x = Doubler(5)
print(x.n() == 10)
assert(x.n() == 10)
y = Doubler(-4)
print(y.n() == -8)
assert(y.n() == -8)True
True
February 3, 2025
Doubler?class Doubler:
def __init__(self, n):
self._n = 2 * n
def n(self):
return self._n
x = Doubler(5)
print(x.n() == 10)
assert(x.n() == 10)
y = Doubler(-4)
print(y.n() == -8)
assert(y.n() == -8)True
True
Doubler classprint or assert statements?print and assertassert statements that will pass and fail as expected, decide which you prefer and why! What situations warrant a print statement and which ones require an assert?print statements require manual checking of outputassert statements automatically checks correctnesspytest or unittestcoverage.pyunittest for DayOfTheWeekimport unittest
from dayoftheweek import DayOfTheWeek
class TestDayOfTheWeek(unittest.TestCase):
def test_init(self):
d = DayOfTheWeek('F')
self.assertEqual(d.name(), 'Friday')
d = DayOfTheWeek('Th')
self.assertEqual(d.name(), 'Thursday')
unittest.main(argv=['ignored'], verbosity=2, exit=False)<unittest.main.TestProgram at 0x7f26660dacc0>
unittest.main differently for tests outside Quartotest_dayoftheweek.py in slides/weekfour/OK output confirms that the assertions passedDayOfTheWeekclass DayOfTheWeek:
"""A class to represent a day of the week."""
def __init__(self, abbreviation):
"""Create a new DayOfTheWeek object."""
self.abbreviation = abbreviation
self.name_map = {
"M": "Monday",
"T": "Tuesday",
"W": "Wednesday",
"Th": "Thursday",
"F": "Friday",
"Sa": "Saturday",
"Su": "Sunday",
}
def name(self):
return self.name_map.get(self.abbreviation)L1 = [1, 2, 3, 4, 5]
L2 = [6, 7, 8, 9, 10]
avg1 = sum(L1)/len(L1)
avg2 = sum(L2)/len(L2)
print("avg(", L1, ") -->", avg1)
print("avg(", L2, ") -->", avg2)avg( [1, 2, 3, 4, 5] ) --> 3.0
avg( [6, 7, 8, 9, 10] ) --> 8.0
L1 = [1, 2, 3, 4, 5]
L2 = [6, 7, 8, 9, 10]
if len(L1) == 0:
avg1 = 0
else:
avg1 = sum(L1) / len(L1)
if len(L2) == 0:
avg2 = 0
else:
avg2 = sum(L2) / len(L2)
print("avg(", L1, ") -->", avg1)
print("avg(", L2, ") -->", avg2)avg( [1, 2, 3, 4, 5] ) --> 3.0
avg( [6, 7, 8, 9, 10] ) --> 8.0
def avg(L):
if len(L) == 0:
return 0
else:
return sum(L) / len(L)
L1 = [1, 2, 3, 4, 5]
L2 = [6, 7, 8, 9, 10]
avg1 = avg(L1)
avg2 = avg(L2)
print("avg(", L1, ") -->", avg1)
print("avg(", L2, ") -->", avg2)avg( [1, 2, 3, 4, 5] ) --> 3.0
avg( [6, 7, 8, 9, 10] ) --> 8.0
avg function avoids the defect and is easier to read!L1 and L2, try to find the defect. Can you make a solution that works for empty lists? How do you know it is correct?coverage.pySoftware testing helps to refine an object-oriented design
Interplay between testing and object-oriented design:
pytesthypothesiscoverage.pymutmutDayOfTheWeek with Pytestfrom daydetector.dayoftheweek import DayOfTheWeek
def test_init():
"""Test the DayOfTheWeek class."""
d = DayOfTheWeek("F")
assert d.name() == "Friday"
d = DayOfTheWeek("Th")
assert d.name() == "Thursday"
d = DayOfTheWeek("W")
assert d.name() == "Wednesday"
d = DayOfTheWeek("T")
assert d.name() == "Tuesday"
d = DayOfTheWeek("M")
assert d.name() == "Monday"Run poetry run pytest in the daydetector directory
pytest will automatically discover and run the tests!
pytest@pytest.mark.parametrize(
"abbreviation, expected",
[
("M", "Monday"),
("T", "Tuesday"),
("W", "Wednesday"),
("Th", "Thursday"),
("F", "Friday"),
("Sa", "Saturday"),
("Su", "Sunday"),
("X", None),
],
)
def test_day_name(abbreviation, expected):
"""Use parameterized testing to confirm that lookup works correctly."""
day = DayOfTheWeek(abbreviation)
assert day.name() == expectedimport hypothesis.strategies as st
from hypothesis import given
import pytest
@pytest.mark.parametrize(
"valid_days",
[["Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday"]],
)
@given(
st.text(alphabet=st.characters(), min_size=1, max_size=2)
)
def test_abbreviation_maps_to_name(valid_days, abbreviation):
"""Use property-based testing with Hypothesis to confirm mapping."""
day = DayOfTheWeek(abbreviation)
assert day.name() in valid_days or day.name() is NoneHypothesis strategies generate random character inputs for the abbreviation parameter, thereby increasing the input diversitycoverage.pytests/test_dayoftheweek.py .......... [100%]
---------- coverage: platform linux, python 3.11.6-final-0 -----------
Name Stmts Miss Branch BrPart Cover
---------------------------------------------------------------
daydetector/__init__.py 0 0 0 0 100%
daydetector/dayoftheweek.py 6 0 0 0 100%
---------------------------------------------------------------
TOTAL 6 0 0 0 100%
10 passed in 0.21sThe test suite covers 100% of the program’s source code
Covering statements increases the likelihood of detecting defects
Do high coverage tests mean that the program is defect-free?
mutmut--- daydetector/dayoftheweek.py
+++ daydetector/dayoftheweek.py
@@ -7,15 +7,7 @@
def __init__(self, abbreviation):
"""Create a new DayOfTheWeek object."""
self.abbreviation = abbreviation
- self.name_map = {
- "M": "Monday",
- "T": "Tuesday",
- "W": "Wednesday",
- "Th": "Thursday",
- "F": "Friday",
- "Sa": "Saturday",
- "Su": "Sunday",
- }
+ self.name_map = Nonemutmut changes the program to see if tests find possible defect
Mutation operators systematically modify the program’s code
www.algorithmology.org/slides/weekfour/poetry installpoetry run pytestpoetry run pytest --cov --cov-branchpoetry run mutmut runpoetry run mutmut show 16test_dayoftheweek.py were written by a large language model?What are the benefits and downsides of using artificial intelligence (AI) to generate tests?
What are situations in which you should and should not use AI to generate tests?
Algorithmology