In [1]:
Copied!
import unittest
import unittest
Set some demo function waiting to test¶
In [2]:
Copied!
def add(x,y):
"""Add Function"""
return x + y
def subtract(x,y):
"""Subtract Function"""
return x - y
def multiply(x,y):
"""Multiply Function"""
return x * y
def divide(x,y):
"""Divide Function"""
if y == 0:
raise ValueError('Can not divide by zero!')
return x / y
def add(x,y):
"""Add Function"""
return x + y
def subtract(x,y):
"""Subtract Function"""
return x - y
def multiply(x,y):
"""Multiply Function"""
return x * y
def divide(x,y):
"""Divide Function"""
if y == 0:
raise ValueError('Can not divide by zero!')
return x / y
Writing Unit Tests¶
- Create a Python File
test_calc.py
- Make a class inherent by
unittest
- Write a
test_[your function]
named function for testing purpose- Example
import unittest import calc class TestCalc(unittest.TestCase): def test_add(self): self.assertEqual(calc.add(10, 5),15) self.assertEqual(calc.add(-1, 1), 0) def test_subtract(self): self.assertEqual(calc.subtract(3, 1), 2) self.assertEqual(calc.subtract(5, 5), 0) def test_multiply(self): self.assertEqual(calc.multiply(10, 5), 50) self.assertEqual(calc.multiply(-1, -1), 1) def test_divide(self): self.assertEqual(calc.divide(10, 5), 2) self.assertEqual(calc.subtract(-1, 1), -1) ## Test Raise Error is right or not self.assertRaises(ValueError,calc.divide,10,0) ## Same to above but different format with self.assertRaises(ValueError): calc.divide(1,0) if __name__ == "__main__": unittest.main()
- Run
python -m unittest [your test module]
In [3]:
Copied!
!python -m unittest test_calc.py
!python -m unittest test_calc.py
.F.. ====================================================================== FAIL: test_divide (test_calc.TestCalc) ---------------------------------------------------------------------- Traceback (most recent call last): File "c:\Users\yxy180050\Box\2024\github\notes\docs\Computer-Science\Software-Design\test_calc.py", line 20, in test_divide self.assertEqual(calc.subtract(-1, 1), -1) AssertionError: -2 != -1 ---------------------------------------------------------------------- Ran 4 tests in 0.002s FAILED (failures=1)
Another Example¶
In [4]:
Copied!
class Employee:
"""A sample Employee class"""
raise_amt = 1.05
def __init__(self, first, last, pay) -> None:
self.first = first
self.last = last
self.pay = pay
@property
def email(self):
return f"{self.first}.{self.last}@email.com"
@property
def fullname(self):
return f"{self.first} {self.last}"
def apply_raise(self):
self.pay = int(self.pay * self.raise_amt)
class Employee:
"""A sample Employee class"""
raise_amt = 1.05
def __init__(self, first, last, pay) -> None:
self.first = first
self.last = last
self.pay = pay
@property
def email(self):
return f"{self.first}.{self.last}@email.com"
@property
def fullname(self):
return f"{self.first} {self.last}"
def apply_raise(self):
self.pay = int(self.pay * self.raise_amt)
SetUp and TearDown (function and class)¶
setUp
Function- The
setUp
function is called before each test method in your test case class. - It's typically used to set up any resources or state needed for the tests to run.
- This could include
creating objects
,opening files
,initializing databases
, or any other necessary preparations. - Example:
import unittest class TestMyClass(unittest.TestCase): def setUp(self): # Set up resources or state needed for tests self.my_list = [1, 2, 3] def test_my_method(self): # Access resources initialized in setUp self.assertEqual(len(self.my_list), 3) self.assertEqual(self.my_list[0], 1) def test_another_method(self): # Access resources initialized in setUp self.assertIn(2, self.my_list)
- The
tearDown
Function- The
tearDown
function is called after each test method in your test case class has been run. - It's used to clean up any resources that were set up in the setUp method to ensure that your tests leave no side effects.
- Example:
import unittest class TestMyClass(unittest.TestCase): def setUp(self): # Set up resources or state needed for tests self.file = open('test_file.txt', 'w') def tearDown(self): # Clean up resources created in setUp self.file.close() # Optionally, delete any temporary files or reset state def test_file_write(self): self.file.write('Test data') self.assertEqual(self.file.tell(), len('Test data'))
- The
- Class-Level
setUp
andtearDown
- Additionally, you can define
setUpClass
andtearDownClass
methods in your test case class to set up and tear down resources once for all test methods in the class. - These methods are called before the first test method is executed and after the last test method is executed, respectively.
- Example:
import unittest class TestMyClass(unittest.TestCase): @classmethod def setUpClass(cls): # Set up resources needed for all test methods in the class cls.db = connect_to_database() @classmethod def tearDownClass(cls): # Clean up resources after all test methods in the class have run cls.db.close() def test_method1(self): # Test method 1 pass def test_method2(self): # Test method 2 pass
Test class for Employee¶
In [5]:
Copied!
class TestEmployee(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
# Run before all test
print("setupClass")
# return super().setUpClass()
@classmethod
def tearDownClass(cls) -> None:
# Run after all test
print("teardownClass")
# return super().tearDownClass()
def setUp(self) -> None:
print("setUp")
## setUp code will run before every test
self.emp_1 = Employee("Corey", "Schafer", 50000)
self.emp_2 = Employee("Sue", "Smith", 60000)
# return super().setUp()
def tearDown(self) -> None:
## tearDown code will run after every test
print('tearDown\n')
# return super().tearDown()
def test_email(self):
print("test_email")
self.assertEqual(self.emp_1.email, "Corey.Schafer@email.com")
self.assertEqual(self.emp_2.email, "Sue.Smith@email.com")
self.emp_1.first = "John"
self.emp_2.first = "Jane"
self.assertEqual(self.emp_1.email, "John.Schafer@email.com")
self.assertEqual(self.emp_2.email, "Jane.Smith@email.com")
def test_fullname(self):
print("test_fullname")
self.assertEqual(self.emp_1.fullname, "Corey Schafer")
self.assertEqual(self.emp_2.fullname, "Sue Smith")
self.emp_1.first = "John"
self.emp_2.first = "Jane"
self.assertEqual(self.emp_1.fullname, "John Schafer")
self.assertEqual(self.emp_2.fullname, "Jane Smith")
def test_apply_raise(self):
print("test_apply_raise")
self.emp_1.apply_raise()
self.emp_2.apply_raise()
self.assertEqual(self.emp_1.pay, 52500)
self.assertEqual(self.emp_2.pay, 63000)
class TestEmployee(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
# Run before all test
print("setupClass")
# return super().setUpClass()
@classmethod
def tearDownClass(cls) -> None:
# Run after all test
print("teardownClass")
# return super().tearDownClass()
def setUp(self) -> None:
print("setUp")
## setUp code will run before every test
self.emp_1 = Employee("Corey", "Schafer", 50000)
self.emp_2 = Employee("Sue", "Smith", 60000)
# return super().setUp()
def tearDown(self) -> None:
## tearDown code will run after every test
print('tearDown\n')
# return super().tearDown()
def test_email(self):
print("test_email")
self.assertEqual(self.emp_1.email, "Corey.Schafer@email.com")
self.assertEqual(self.emp_2.email, "Sue.Smith@email.com")
self.emp_1.first = "John"
self.emp_2.first = "Jane"
self.assertEqual(self.emp_1.email, "John.Schafer@email.com")
self.assertEqual(self.emp_2.email, "Jane.Smith@email.com")
def test_fullname(self):
print("test_fullname")
self.assertEqual(self.emp_1.fullname, "Corey Schafer")
self.assertEqual(self.emp_2.fullname, "Sue Smith")
self.emp_1.first = "John"
self.emp_2.first = "Jane"
self.assertEqual(self.emp_1.fullname, "John Schafer")
self.assertEqual(self.emp_2.fullname, "Jane Smith")
def test_apply_raise(self):
print("test_apply_raise")
self.emp_1.apply_raise()
self.emp_2.apply_raise()
self.assertEqual(self.emp_1.pay, 52500)
self.assertEqual(self.emp_2.pay, 63000)
In [6]:
Copied!
!python -m unittest test_employee.py
!python -m unittest test_employee.py
setupClass
... ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK
setUp test_apply_raise tearDown setUp test_email tearDown setUp test_fullname tearDown teardownClass
Mock (Dealing with online materials)¶
- Sometime we and dealing with some online data, our method might work well but their webpage might shutdown, we do not our code test failed in this situation since there's nothing we can do about it, we need to use
mock
- It allows you to replace parts of your code with mock objects, enabling you to isolate the code you're testing from its dependencies.
- In the following example, we will use
patch
fromunittest.mock
to mock therequests.get
function.- This allows us to simulate a successful HTTP GET request without actually making a network call.
In [7]:
Copied!
import requests
class Employee:
"""A sample Employee class"""
raise_amt = 1.05
def __init__(self, first, last, pay) -> None:
self.first = first
self.last = last
self.pay = pay
@property
def email(self):
return f"{self.first}.{self.last}@email.com"
@property
def fullname(self):
return f"{self.first} {self.last}"
def apply_raise(self):
self.pay = int(self.pay * self.raise_amt)
## get info from url
def monthly_schedule(self, month):
response = requests.get(f"http://company.com/{self.last}/{month}")
if response.ok:
return response.text
else:
return "Bad Response!"
import requests
class Employee:
"""A sample Employee class"""
raise_amt = 1.05
def __init__(self, first, last, pay) -> None:
self.first = first
self.last = last
self.pay = pay
@property
def email(self):
return f"{self.first}.{self.last}@email.com"
@property
def fullname(self):
return f"{self.first} {self.last}"
def apply_raise(self):
self.pay = int(self.pay * self.raise_amt)
## get info from url
def monthly_schedule(self, month):
response = requests.get(f"http://company.com/{self.last}/{month}")
if response.ok:
return response.text
else:
return "Bad Response!"
In [8]:
Copied!
from unittest.mock import patch
class TestEmployee(unittest.TestCase):
def setUp(self):
# Create an instance of the Employee class for testing
self.emp_1 = Employee("John", "Doe")
def test_monthly_schedule(self):
# Patching requests.get to mock the HTTP GET request
with patch("employee02.requests.get") as mocked_get:
# Configure the mock object's behavior
mocked_get.return_value.ok = True
mocked_get.return_value.text = "Success"
# Call the method being tested
schedule = self.emp_1.monthly_schedule("May")
# Assert that requests.get was called with the correct URL
mocked_get.assert_called_with("http://company.com/Doe/May")
# Assert the expected result
self.assertEqual(schedule, "Success")
## simulate connect failed
mocked_get.return_value.ok = False
schedule = self.emp_2.monthly_schedule("June")
mocked_get.assert_called_with("http://company.com/Smith/June")
self.assertEqual(schedule,"Bad Response!")
from unittest.mock import patch
class TestEmployee(unittest.TestCase):
def setUp(self):
# Create an instance of the Employee class for testing
self.emp_1 = Employee("John", "Doe")
def test_monthly_schedule(self):
# Patching requests.get to mock the HTTP GET request
with patch("employee02.requests.get") as mocked_get:
# Configure the mock object's behavior
mocked_get.return_value.ok = True
mocked_get.return_value.text = "Success"
# Call the method being tested
schedule = self.emp_1.monthly_schedule("May")
# Assert that requests.get was called with the correct URL
mocked_get.assert_called_with("http://company.com/Doe/May")
# Assert the expected result
self.assertEqual(schedule, "Success")
## simulate connect failed
mocked_get.return_value.ok = False
schedule = self.emp_2.monthly_schedule("June")
mocked_get.assert_called_with("http://company.com/Smith/June")
self.assertEqual(schedule,"Bad Response!")
In [9]:
Copied!
!python -m unittest test_employee2.py
!python -m unittest test_employee2.py
setupClass setUp test_apply_raise tearDown setUp test_email tearDown setUp test_fullname tearDown setUp tearDown teardownClass
.... ---------------------------------------------------------------------- Ran 4 tests in 0.002s OK