Skip to content

Commit

Permalink
Merge pull request #515 from realpython/python-unittest
Browse files Browse the repository at this point in the history
Sample code for the article on `unittest`
  • Loading branch information
brendaweles authored Apr 15, 2024
2 parents 2218306 + 3162644 commit 27b2cce
Show file tree
Hide file tree
Showing 30 changed files with 740 additions and 0 deletions.
3 changes: 3 additions & 0 deletions python-unittest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Python's unittest: Writing Unit Tests for Your Code

This folder provides the code examples for the Real Python tutorial [Python's unittest: Writing Unit Tests for Your Code](https://realpython.com/python-unittest/).
11 changes: 11 additions & 0 deletions python-unittest/age.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
def categorize_by_age(age):
if 0 <= age <= 9:
return "Child"
elif 9 < age <= 18:
return "Adolescent"
elif 18 < age <= 65:
return "Adult"
elif 65 < age <= 150:
return "Golden age"
else:
return f"Invalid age: {age}"
36 changes: 36 additions & 0 deletions python-unittest/calculations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from collections import Counter


def add(x, y):
return x + y


def subtract(x, y):
return x - y


def multiply(x, y):
return x * y


def divide(x, y):
if y == 0:
raise ZeroDivisionError("Cannot divide by zero.")
return x / y


def mean(data):
return sum(data) / len(data)


def median(data):
n = len(data)
index = n // 2
if n % 2:
return sorted(data)[index]
return sum(sorted(data)[index - 1 : index + 1]) / 2


def mode(data):
c = Counter(data)
return [k for k, v in c.items() if v == c.most_common(1)[0][1]]
38 changes: 38 additions & 0 deletions python-unittest/employee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import csv


class Employee:
__slots__ = ["name", "age", "job", "salary"]

def __init__(self, name, age, job, salary):
self.name = name
self.age = age
self.job = job
self.salary = salary

def profile(self):
for attr in self.__slots__:
print(f"{attr.capitalize()}: {getattr(self, attr)}")
print()


def from_csv_file(file_path):
with open(file_path) as file:
reader = csv.DictReader(file)
employees = []
for row in reader:
employees.append(
Employee(
name=row["name"],
age=int(row["age"]),
job=row["job"],
salary=float(row["salary"]),
)
)
return employees


if __name__ == "__main__":
employees = from_csv_file("employees.csv")
for employee in employees:
employee.profile()
5 changes: 5 additions & 0 deletions python-unittest/employees.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name,age,job,salary
Alice,25,Engineer,50000
Bob,30,Analyst,60000
Jane,35,Manager,80000
John,40,CEO,100000
2 changes: 2 additions & 0 deletions python-unittest/even.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def is_even(number):
return number % 2 == 0
20 changes: 20 additions & 0 deletions python-unittest/fizzbuzz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# def fizzbuzz(number):
# if number % 3 == 0:
# return "fizz"
# elif number % 5 == 0:
# return "buzz"
# elif number % 15 == 0:
# return "fizz buzz"
# else:
# return number


def fizzbuzz(number):
if number % 15 == 0:
return "fizz buzz"
elif number % 3 == 0:
return "fizz"
elif number % 5 == 0:
return "buzz"
else:
return number
6 changes: 6 additions & 0 deletions python-unittest/game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def rock_paper_scissors(choice):
if choice < 0 or choice > 2:
raise ValueError("number must be 0, 1, or 2")

choices = ["rock", "paper", "scissors"]
return choices[choice]
10 changes: 10 additions & 0 deletions python-unittest/prime_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import math


def is_prime(number):
if number <= 1:
return False
for i in range(2, int(math.sqrt(number)) + 1):
if number % i == 0:
return False
return True
14 changes: 14 additions & 0 deletions python-unittest/prime_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from math import sqrt


def is_prime(number):
if not isinstance(number, int):
raise TypeError(
f"integer number expected, got {type(number).__name__}"
)
if number < 2:
raise ValueError(f"integer above 1 expected, got {number}")
for candidate in range(2, int(sqrt(number)) + 1):
if number % candidate == 0:
return False
return True
24 changes: 24 additions & 0 deletions python-unittest/skip_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import sys
import unittest


class SkipTestExample(unittest.TestCase):
@unittest.skip("Unconditionally skipped test")
def test_unimportant(self):
self.fail("The test should be skipped")

@unittest.skipIf(sys.version_info < (3, 12), "Requires Python >= 3.12")
def test_using_calendar_constants(self):
import calendar

self.assertEqual(calendar.Month(10), calendar.OCTOBER)

@unittest.skipUnless(sys.platform.startswith("win"), "Requires Windows")
def test_windows_support(self):
from ctypes import WinDLL, windll

self.assertIsInstance(windll.kernel32, WinDLL)


if __name__ == "__main__":
unittest.main(verbosity=2)
18 changes: 18 additions & 0 deletions python-unittest/stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Stack:
def __init__(self, items=None):
self.items = list(items) if items is not None else []

def push(self, item):
self.items.append(item)

def pop(self):
return self.items.pop()

def __len__(self):
return len(self.items)

def __iter__(self):
return iter(self.items)

def __reversed__(self):
return reversed(self.items)
48 changes: 48 additions & 0 deletions python-unittest/test_age.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import unittest

from age import categorize_by_age


class TestCategorizeByAge(unittest.TestCase):
def test_child(self):
"""Test for 'Child'"""
self.assertEqual(categorize_by_age(5), "Child")

def test_adolescent(self):
"""Test for 'Adolescent'"""
self.assertEqual(categorize_by_age(15), "Adolescent")

def test_adult(self):
"""Test for 'Adult'"""
self.assertEqual(categorize_by_age(30), "Adult")

def test_golden_age(self):
"""Test for 'Golden age'"""
self.assertEqual(categorize_by_age(70), "Golden age")

def test_negative_age(self):
"""Test for negative age"""
self.assertEqual(categorize_by_age(-1), "Invalid age: -1")

def test_too_old(self):
"""Test for too old"""
self.assertEqual(categorize_by_age(151), "Invalid age: 151")

def test_boundary_child_adolescent(self):
"""Test for boundary between 'Child' and 'Adolescent'"""
self.assertEqual(categorize_by_age(9), "Child")
self.assertEqual(categorize_by_age(10), "Adolescent")

def test_boundary_adolescent_adult(self):
"""Test for boundary between 'Adolescent' and 'Adult'"""
self.assertEqual(categorize_by_age(18), "Adolescent")
self.assertEqual(categorize_by_age(19), "Adult")

def test_boundary_adult_golden_age(self):
"""Test for boundary between 'Adult' and 'Golden age'"""
self.assertEqual(categorize_by_age(65), "Adult")
self.assertEqual(categorize_by_age(66), "Golden age")


if __name__ == "__main__":
unittest.main(verbosity=2)
94 changes: 94 additions & 0 deletions python-unittest/test_calculations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import unittest

from calculations import add, divide, mean, median, mode, multiply, subtract


class TestArithmeticOperations(unittest.TestCase):
def test_add(self):
self.assertEqual(add(10, 5), 15)
self.assertEqual(add(-1, 1), 0)

def test_subtract(self):
self.assertEqual(subtract(10, 5), 5)
self.assertEqual(subtract(-1, 1), -2)

def test_multiply(self):
self.assertEqual(multiply(10, 5), 50)
self.assertEqual(multiply(-1, 1), -1)

def test_divide(self):
self.assertEqual(divide(10, 5), 2)
self.assertEqual(divide(-1, 1), -1)
with self.assertRaises(ZeroDivisionError):
divide(10, 0)


class TestStatisticalOperations(unittest.TestCase):
def test_mean(self):
self.assertEqual(mean([1, 2, 3, 4, 5, 6]), 3.5)

def test_median_odd(self):
self.assertEqual(median([1, 3, 3, 6, 7, 8, 9]), 6)

def test_median_even(self):
self.assertEqual(median([1, 2, 3, 4, 5, 6, 8, 9]), 4.5)

def test_median_unsorted(self):
self.assertEqual(median([7, 1, 3, 3, 2, 6]), 3)

def test_mode_single(self):
self.assertEqual(mode([1, 2, 2, 3, 4, 4, 4, 5]), [4])

def test_mode_multiple(self):
self.assertEqual(set(mode([1, 1, 2, 3, 4, 4, 5, 5])), {1, 4, 5})


# def make_suite():
# arithmetic_tests = [
# TestArithmeticOperations("test_add"),
# TestArithmeticOperations("test_subtract"),
# TestArithmeticOperations("test_multiply"),
# TestArithmeticOperations("test_divide"),
# ]
# return unittest.TestSuite(tests=arithmetic_tests)


# def make_suite():
# arithmetic_suite = unittest.TestSuite()
# arithmetic_suite.addTest(TestArithmeticOperations("test_add"))
# arithmetic_suite.addTest(TestArithmeticOperations("test_subtract"))
# arithmetic_suite.addTest(TestArithmeticOperations("test_multiply"))
# arithmetic_suite.addTest(TestArithmeticOperations("test_divide"))

# return arithmetic_suite


# def make_suite():
# statistical_tests = [
# TestStatisticalOperations("test_mean"),
# TestStatisticalOperations("test_median_odd"),
# TestStatisticalOperations("test_median_even"),
# TestStatisticalOperations("test_median_unsorted"),
# TestStatisticalOperations("test_mode_single"),
# TestStatisticalOperations("test_mode_multiple"),
# ]
# statistical_suite = unittest.TestSuite()
# statistical_suite.addTests(statistical_tests)

# return statistical_suite

# if __name__ == "__main__":
# suite = make_suite()
# runner = unittest.TextTestRunner(verbosity=2)
# runner.run(suite)


def load_tests(loader, standard_tests, pattern):
suite = unittest.TestSuite()
suite.addTests(loader.loadTestsFromTestCase(TestArithmeticOperations))
suite.addTests(loader.loadTestsFromTestCase(TestStatisticalOperations))
return suite


if __name__ == "__main__":
unittest.main()
37 changes: 37 additions & 0 deletions python-unittest/test_collections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import unittest


class TestCollections(unittest.TestCase):
def test_sequence_objects(self):
a = ("H", "e", "l", "l", "o")
b = "Hello"
self.assertSequenceEqual(a, b)

def test_string_objects(self):
a = "Hello"
b = "Hello"
self.assertMultiLineEqual(a, b)

def test_list_objects(self):
a = [1, 2, 3, 4, 5]
b = [1, 2, 3, 4, 5]
self.assertListEqual(a, b)

def test_tuple_objects(self):
a = ("Jane", 25, "New York")
b = ("Jane", 25, "New York")
self.assertTupleEqual(a, b)

def test_dictionary_objects(self):
a = {"framework": "unittest", "language": "Python"}
b = {"language": "Python", "framework": "unittest"}
self.assertDictEqual(a, b)

def test_set_objects(self):
a = {1, 2, 4, 3, 5}
b = {1, 5, 3, 4, 2}
self.assertSetEqual(a, b)


if __name__ == "__main__":
unittest.main(verbosity=2)
Loading

0 comments on commit 27b2cce

Please sign in to comment.