Testing and Debugging Notes
Review from Last Class
The previous class involved writing a Python program to find unique combinations of 3 numbers from a list that sum to a target number.
The task included providing doctests for specific test cases.
Example:
List:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Target: 12
Combinations:
[(1, 3, 8), (1, 4, 7), (1, 2, 9), (1, 5, 6), (3, 4, 5), (2, 3, 7), (2, 4, 6)]
Example:
List:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Target: 17
Combinations:
[(4, 5, 8), (2, 6, 9), (3, 5, 9), (2, 7, 8), (4, 6, 7), (3, 6, 8), (1, 7, 9)]
Unit Testing
Unit testing involves testing individual units or components of code to ensure proper functioning.
The correct approach to unit testing is to test all code parts individually.
Key Aspects of Unit Testing:
All tests should be within a subclass
TestCase
from theunittest
library.Each test is a function named starting with
test_
.Assert methods are used to check for expected results.
The
setUp()
method is called before each test, used to define elements needed for the tests.The
tearDown()
method is called after each test, used to reset definitions after each test.
Assert Methods:
assertTrue
: Succeeds if the expression is true.assertFalse
: Succeeds if the expression is false.assertEqual
,assertNotEqual
: Checks equality and inequality.assertAlmostEqual
: Used for comparing floating-point numbers.assertNotAlmostEqual
Running Tests in VSCode:
Create a workspace where all tests are located.
Configure tests in the testing tab.
Select
unittest
-> Root directory ->test_*.py
.
Troubleshooting:
If test files are not appearing, reload the window:
Control + Shift + p
(Windows)Command + Shift + p
(Mac)Options: "Reload window", "Configure python tests"
If the explorer is not working, run tests in the terminal:
python -m unittest discover -v -s . -p "test_*.py"
For test discovery errors, reload the window.
Tips:
Create a new folder for each exercise.
Configure Python tests in each exercise by changing the folder in the configuration.
Each exercise should have at least two files:
class_name.py
andtest_class_name.py
.
Exercises
Exercise 1 - Calculator Class:
Step 1: Create tests for all methods (
add
,subtract
,multiply
,divide
) usingpytest
andunittest
.Step 2: Implement the functions to make tests pass.
Test cases provided for each method with expected results.
For example:
add(1, 3)
should return 4.subtract(8, 7)
should return 1.multiply(8, 3)
should return 24.divide(10, 5)
should return 2.
Exercise 2 - Temperature Converter Class:
Step 1: Create tests for all methods (
celsius_to_fahrenheit
,fahrenheit_to_celsius
,celsius_to_kelvin
,kelvin_to_celsius
) usingpytest
andunittest
.Step 2: Implement the functions to make tests pass.
Test cases provided for each method with expected results.
celsius_to_fahrenheit(0)
should return 32.fahrenheit_to_celsius(32)
should return 0.celsius_to_kelvin(25)
should return 298.15.kelvin_to_celsius(273.15)
should return 0.
PyTest
Supports unittests.
Allows selecting specific tests using the
-k
flag.Provides mark tests:
@pytest.mark.skip
: Skips the test.@pytest.mark.xfail
: Marks the test as expected to fail.
Filter by marked tests using the
-m
flag.Run from the last failed test using the
--lf
flag.
Mocking
Mock objects imitate real objects but with fake data.
Commonly used for API calls.
Decouples tested code from other calls.
Example:
Mocking an expensive API call to speed up tests.
import time def compute(x): response = expensive_api_call() return response + x def expensive_api_call(): time.sleep(1000) # takes 1,000 seconds return 123 import unittest import compute_expensive class TestComputeExpensive(unittest.TestCase): def test_compute(self): self.assertEqual(compute_expensive.compute(1), 124) import unittest from unittest.mock import patch import compute_expensive class TestComputeExpensive(unittest.TestCase): @patch('compute_expensive.expensive_api_call', return_value=123) def test_compute(self, mock_expensive_api_call): # calls the compute function mocking the expensive_api_call self.assertEqual(124, compute_expensive.compute(1)) # asserts that expensive_api_call was called once mock_expensive_api_call.assert_called_once()
Code Coverage
Tracks which lines of code are executed during tests.
Provides a report describing test coverage.
Tool to gain insight into what the tests are doing.
Not the sole definition of a good test suite.
How to Run Test Coverage:
Right-click in the folder where test folders are located and select "Run Tests with Coverage".
If a
No module named 'coverage'
error occurs, install the coverage package:pip install coverage
orpip3 install coverage
ifpip
is not working.
The percentage of code covered appears next to the file name once configured correctly.
Exercises
Exercise 3 - Bank Account Class:
Step 1: Create test cases for
BankAccount
class methods (__init__
,deposit
,withdraw
,get_balance
).Step 2: Implement failing tests.
Step 3: Implement the code to make the tests pass.
Exercise 4 - Counter Class:
Step 1: Create test cases for
Counter
class methods (__init__
,increment
,decrement
,reset
,get_value
).Step 2: Implement failing tests.
Step 3: Implement the code to make the tests pass.
Exercise 5 - To-Do List Class:
Step 1: Create test cases for
ToDoList
class methods (__init__
,add_task
,remove_task
,get_tasks
).Step 2: Implement failing tests.
Step 3: Implement the code to make the tests pass.
Exercise 6 - Shapes:
Create tests using inheritance and abstract methods for shapes (rectangle, circle, square).
Each shape should have perimeter and area calculations.