Computer ScienceChapter 3: Programming Fundamentals
3.3.2 Python Unit Test
Unit Testing
Explanation level
Introduction
When writing programs, it is not enough to simply run the code and hope it works. A more reliable approach is to write tests that automatically check whether the code produces the correct output. Python's built-in `unittest` framework is a tool that helps you check if your code works in the desired way.
The textbook explains that rather than manually testing your code by running it, `unittest` lets you write small tests that check specific parts of your program. You may check it for some values to see whether it is returning the desired and correct result. But as the lines of code increase, the code may become complicated to test manually. `unittest` is quite useful in such scenarios to write a set of tests to ensure the function is executing correctly for various inputs.
Definition
Unit Testing is a software testing technique where individual units or components of a program (such as functions or methods) are tested in isolation to verify that they work correctly.
In simple words: Unit testing means writing small tests to check if individual parts of your code (like functions) give the correct results.
Main Idea
- Write a function that performs some operation.
- Write another function (a test function) that checks if the first function gives the correct result.
- Use assertions to compare the actual output with the expected output.
- Run the tests to see if they pass or fail.
The textbook emphasizes that unit testing helps catch errors early during development. When a test fails, it tells you exactly what went wrong and where, making it easier to fix the problem.
Core Concept
- Write small tests that check specific parts of your program.
- Use assertions to compare actual output with expected output.
- Run tests automatically and see which ones pass or fail.
The textbook explains that `unittest` lets you write a set of tests to ensure the function is executing correctly for various inputs. This is especially useful as the lines of code increase and manual testing becomes complicated.
Writing a Unit Test
The textbook provides a complete example of unit testing:
Step 1: The function to be tested (line 1): ```python def add(a, b): # Function to be tested return a + b ``` This is a simple function that adds two numbers.
Step 2: The test function (lines 4–14): ```python def test_add(): # Test function result = add(2, 3) # Test case 1: Check if 2 + 3 equals 5 assert result == 5, f"Test failed: Expected 5 but got {result}"
result = add(0, 0) # Test case 2: Check if 0 + 0 equals 0 assert result == 0, f"Test failed: Expected 0 but got {result}"
result = add(-1, 1) # Test case 3: Check if -1 + 1 equals 0 assert result == 0, f"Test failed: Expected 0 but got {result}"
print("All tests passed!") ```
Step 3: Running the test (line 16): ```python test_add() # Run the test function ```
Explanation of the Code (from the book)
- Line 1: The `add()` function adds two numbers.
- Line 4: The `test_add()` function tests the `add()` function with different inputs.
- Line 5: Two positive numbers are provided as input parameters.
- Line 8: Two null values are provided.
- Line 11: A positive and negative number are provided.
- Assert statement: Checks if the output of `add()` matches the expected value. If not, it raises an error with a custom message.
- Line 14: If all assertions pass, it prints "All tests passed!"
The `assert` Statement The `assert` statement is a key part of unit testing. It takes a condition and an optional error message: ```python assert condition, "Error message" ```
- If the condition is True, the test passes (nothing happens).
- If the condition is False, an `AssertionError` is raised with the error message.
Example: ```python assert result == 5, f"Test failed: Expected 5 but got {result}" ``` This checks if `result` equals `5`. If not, it raises an error with the message "Test failed: Expected 5 but got [actual result]".
Example: Testing a Multiplication Function The textbook also provides an example of testing a multiplication function (Fig. 3.34): ```python def multiply(a, b): return a * b
def test_multiply(): result = multiply(2, 3) assert result == 6, f"Test failed: Expected 6 but got {result}"
result = multiply(4, 0) assert result == 1, f"Test failed: Expected 1 but got {result}"
result = multiply(-1, -1) assert result == 1, f"Test failed: Expected 1 but got {result}"
print("All tests passed!")
test_multiply() ```
Question from the book: Will all tests be passed? If not, which one will fail and why?
- `multiply(4, 0)` returns `0`, but the test expects `1`.
- The correct expected value should be `0`.
- This is a bug in the test, not in the function.
This example reinforces the lesson: always verify that your tests are correct.
Debugging a Failed Test The textbook demonstrates what happens when a test fails and how to debug it.
Example of a failed test (Fig. 3.31–3.33): Suppose the expected result on line 12 is wrong (expected 2 instead of 0): ```python result = add(-1, 1) assert result == 2, f"Test failed: Expected 2 but got {result}" ```
When the test is run, it fails: ``` Test failed: Expected 2 but got 0 ```
- The programmer places breakpoints on lines 6, 9, and 12.
- A watchpoint is added on the `result` variable.
- When the debugger runs, it stops at each breakpoint, showing the value of `result`.
- At the first breakpoint (line 6), `result` is 5 — as expected.
- At the second breakpoint (line 9), `result` is 0 — as expected.
- At the third breakpoint (line 12), `result` is 0, but the expected value is 2 — revealing the bug in the test.
Explanation: The bug is not in the `add()` function — it works correctly. The bug is in the test itself — the expected value is wrong. This shows that debugging can reveal errors in test cases, not just in the code being tested.
Why Unit Testing is Important The textbook explains that unit testing helps in catching errors early during the development process. As the lines of code increase, the code may become complicated to test manually. Unit testing is useful in such scenarios to write a set of tests to ensure the function is executing correctly for various inputs.
- Catches errors early — Before they become complex and hard to fix.
- Automatic — No need to manually run the program with different inputs.
- Specific — Tests check individual functions, making it easier to locate problems.
- Repeatable — Tests can be run again after changes to ensure nothing is broken.
- Documentation — Tests serve as examples of how the function is supposed to work.
Intermediate Level
The textbook demonstrates the structure of a unit test:
| Component | Purpose | Example |
|---|---|---|
| Function to be tested | The code that performs the operation | `def add(a, b): return a + b` |
| Test function | Checks if the function works correctly | `def test_add(): ...` |
| Assert statement | Compares actual output with expected output | `assert result == 5, "Error"` |
| Test cases | Different inputs to test | `add(2, 3)`, `add(0, 0)`, `add(-1, 1)` |
| Error message | Custom message if the test fails | `f"Test failed: Expected 5 but got {result}"` |
Key point: The test function is separate from the function being tested. This separation ensures that the test is checking the function objectively, without relying on the function's internal logic.
Important Points
- - Unit Testing: Testing individual units or components of a program in isolation.
- - `unittest` Framework: Python's built-in tool for unit testing.
- - `assert` Statement: Checks if a condition is true; raises an error if not.
- - Test Function: A function that tests another function.
- - Test Cases: Different inputs to test the function.
- - Error Message: Custom message displayed when a test fails.
- - Debugging Failed Tests: Use breakpoints and watchpoints to find the cause.
- - Verify Tests: The bug might be in the test itself, not the code.
- - Benefits: Catches errors early, automatic, specific, repeatable, documentation.
Remember
- - Unit Testing = Testing individual functions.
- - `unittest` = Python's testing framework.
- - `assert` = Check if condition is true.
- - Test function = Function that tests another function.
- - Test cases = Different inputs.
- - Error message = Custom message on failure.
- - Debug failed tests = Use breakpoints and watchpoints.
- - Verify tests = The bug might be in the test.
- - Benefits: Early error detection, automatic, specific, repeatable.
Common Mistakes
- 1. Not testing enough cases — Testing only one input is not enough; test with positive, negative, zero, and edge cases.
- 2. Writing wrong expected values — The test can have bugs too; always verify the expected values.
- 3. Not using error messages — Error messages help identify what went wrong; always include them.
- 4. Testing multiple functions in one test — Each test should focus on one function or one aspect.
- 5. Ignoring failed tests — A failed test is a sign of a problem; don't ignore it.
- 6. Not running tests after changes — Always run tests after making changes to ensure nothing is broken.
Quick Revision
- - Unit Testing: A software testing technique where individual units or components of a program are tested in isolation to verify that they work correctly.
- - `unittest` Framework: Python's built-in tool for unit testing.
- - Writing a Unit Test:
- 1. Write the function to be tested.
- 2. Write a test function that calls the function with different inputs.
- 3. Use `assert` statements to compare actual output with expected output.
- 4. Run the test function.
- - `assert` Statement: `assert condition, "Error message"` — checks if condition is true; raises an error if not.
- - Example:
- ```python
- def add(a, b):
- return a + b
- def test_add():
- result = add(2, 3)
- assert result == 5, f"Test failed: Expected 5 but got {result}"
- result = add(0, 0)
- assert result == 0, f"Test failed: Expected 0 but got {result}"
- result = add(-1, 1)
- assert result == 0, f"Test failed: Expected 0 but got {result}"
- print("All tests passed!")
- Debugging Failed Tests: Use breakpoints and watchpoints to find the cause.
- Verify Tests: The bug might be in the test itself, not the code.
- Benefits: Catches errors early, automatic, specific, repeatable, documentation.
60-Second Revision
- Unit Testing = Testing individual functions.
- `unittest` = Python's testing framework.
- `assert` = Check if condition is true.
- Test function = Function that tests another function.
- Test cases = Different inputs.
- Error message = Custom message on failure.
- Debug failed tests = Use breakpoints and watchpoints.
- Warning: Verify tests — the bug might be in the test.
Frequently Asked Questions (FAQs)
Q1. What is unit testing? Answer: Unit testing is a software testing technique where individual units or components of a program (such as functions or methods) are tested in isolation to verify that they work correctly. It helps catch errors early during development.
Q2. What is Python's `unittest` framework? Answer: Python's built-in `unittest` framework is a tool that helps you check if your code works in the desired way. It allows you to write small tests that check specific parts of your program, use assertions to compare actual output with expected output, and run tests automatically.
Q3. What is an `assert` statement? Answer: An `assert` statement is used to check if a condition is true. If the condition is true, the test passes. If the condition is false, an `AssertionError` is raised with an error message. For example: `assert result == 5, f"Test failed: Expected 5 but got {result}"`.
- Write the function to be tested.
- Write a test function that calls the function with different inputs.
- Use `assert` statements to compare actual output with expected output.
- Run the test function.
Q5. What are test cases? Answer: Test cases are different inputs used to test a function. For example, for an `add()` function, test cases might include `add(2, 3)`, `add(0, 0)`, and `add(-1, 1)`.
Q6. What happens when a unit test fails? Answer: When a unit test fails, an `AssertionError` is raised with the error message. This tells the programmer that the function did not produce the expected output. Debugging tools like breakpoints and watchpoints can then be used to find the cause.
- Place breakpoints on the lines with the assertions.
- Add a watchpoint on the variable being tested.
- Run the debugger and step through the code.
- Inspect the value of the variable at each breakpoint.
- Compare the actual value with the expected value to find the bug.
Q8. Why is it important to verify that the test itself is correct? Answer: It is important to verify that the test itself is correct because the bug might be in the test, not the code. For example, if the expected value in the test is wrong, the test will fail even if the function is correct. Always double-check the expected values in your tests.
- Catches errors early during development.
- Automatic (no need to manually run the program with different inputs).
- Specific (tests check individual functions, making it easier to locate problems).
- Repeatable (tests can be run again after changes).
- Documentation (tests serve as examples of how the function is supposed to work).
Q10. What is the difference between unit testing and debugging? Answer: Unit testing is the process of writing and running tests to check if individual functions work correctly. Debugging is the process of finding and fixing the cause of a failed test or a bug in the code. Unit testing helps identify that a problem exists; debugging helps find and fix the problem.
- `multiply(4, 0)` returns `0`, but the test expects `1`.
- The correct expected value should be `0`.
- This is a bug in the test, not in the function.
Practice Questions
Short Response Questions (SRQs)
- Define unit testing and explain its importance.
- What is Python's `unittest` framework?
- Explain the `assert` statement with an example.
- How do you write a test function for a simple `add()` function?
- Why is it important to verify that the test itself is correct?
Extended Response Questions (ERQs)
- Discuss the concept of unit testing in Python. Why is it important for software development?
- Explain how to write and run a unit test for a function. Provide a complete example with multiple test cases.
- Analyze the importance of the `assert` statement in unit testing. How does it help catch errors?
- You are writing a Python program to calculate the area of a rectangle. Describe how you would write unit tests for the `area()` function, including test cases for different inputs.
- Explain how debugging tools (breakpoints, watchpoints) can be used to debug a failed unit test. Provide an example.
Multiple Choice Questions (MCQs)
- Q1. What is unit testing?
- A. Testing the entire program at once
- B. Testing individual units or components of a program
- C. Testing the hardware
- D. Testing the operating system
- Correct Answer: B
- Explanation: Unit testing involves testing individual units or components of a program in isolation.
- Q2. Which framework is used for unit testing in Python?
- A. NumPy
- B. `unittest`
- C. Pandas
- D. Matplotlib
- Correct Answer: B
- Explanation: Python's built-in `unittest` framework is used for unit testing.
- Q3. What is the purpose of the `assert` statement?
- A. To print output
- B. To check if a condition is true
- C. To create a loop
- D. To define a function
- Correct Answer: B
- Explanation: The `assert` statement checks if a condition is true and raises an error if it is false.
- Q4. What happens when an `assert` statement fails?
- A. The program continues
- B. An `AssertionError` is raised
- C. The program restarts
- D. The program prints a warning
- Correct Answer: B
- Explanation: When an `assert` statement fails, an `AssertionError` is raised with the error message.
- Q5. What is a test case?
- A. A specific input used to test a function
- B. A type of function
- C. A type of loop
- D. A type of variable
- Correct Answer: A
- Explanation: A test case is a specific input used to test a function.
- Q6. What is the output of the following code?
- ```python
- def add(a, b):
- return a + b
- result = add(2, 3)
- assert result == 5, "Test failed"
- print("Test passed")
- ```
- A. Test failed
- B. Test passed
- C. AssertionError
- D. No output
- Correct Answer: B
- Explanation: The assertion passes because `2 + 3 == 5`, so "Test passed" is printed.
- Q7. What happens if the expected value in a test is wrong?
- A. The function is wrong
- B. The test fails even if the function is correct
- C. The test passes
- D. The function is fixed automatically
- Correct Answer: B
- Explanation: If the expected value in a test is wrong, the test will fail even if the function is correct. The bug is in the test, not the function.
- Q8. How can you debug a failed unit test?
- A. Use breakpoints and watchpoints
- B. Ignore the failure
- C. Delete the test
- D. Rewrite the function
- Correct Answer: A
- Explanation: Breakpoints and watchpoints can be used to debug a failed unit test by inspecting variable values.
- Q9. Why is unit testing important?
- A. It makes the program run faster
- B. It catches errors early during development
- C. It reduces the size of the code
- D. It eliminates the need for debugging
- Correct Answer: B
- Explanation: Unit testing catches errors early during development, making them easier to fix.
- Q10. What is the benefit of using error messages in `assert` statements?
- A. They make the test run faster
- B. They help identify what went wrong
- C. They make the code shorter
- D. They eliminate the need for debugging
- Correct Answer: B
- Explanation: Error messages in `assert` statements help identify what went wrong when a test fails.
- Q11. In the multiplication example, which test fails?
- A. `multiply(2, 3)` expected 6
- B. `multiply(4, 0)` expected 1
- C. `multiply(-1, -1)` expected 1
- D. All tests pass
- Correct Answer: B
- Explanation: `multiply(4, 0)` returns `0`, but the test expects `1`. The correct expected value should be `0`.
- Q12. What does the `test_multiply()` example teach?
- A. The code is always wrong
- B. The bug might be in the test itself
- C. Unit tests are not useful
- D. Debugging is not necessary
- Correct Answer: B
- Explanation: The example teaches that the bug might be in the test itself, not just in the code.
Did you understand this topic?
Your answer is saved with your learning record. It does not mark the topic as mastered.
Quick revision
Revision Zone
Review the key facts, formulas and checks before you move on.3.3.2 Python Unit Test
Sticky Notes · Quick Revision
Key Point 1
- Unit Testing = Testing individual functions.
Key Point 2
- `unittest` = Python's testing framework.
Key Point 3
- `assert` = Check if condition is true.
Key Point 4
- Test function = Function that tests another function.
Key Point 5
- Test cases = Different inputs.
Key Point 6
- Error message = Custom message on failure.
Key Point 7
- Debug failed tests = Use breakpoints and watchpoints.
Key Point 8
- Verify tests = The bug might be in the test.
Key Point 9
- Benefits: Early error detection, automatic, specific, repeatable.
Same supplied content as this Notes page (shared canonical source).