-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #515 from realpython/python-unittest
Sample code for the article on `unittest`
- Loading branch information
Showing
30 changed files
with
740 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
def is_even(number): | ||
return number % 2 == 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Oops, something went wrong.