Slide 1: Happy Path Testing
Happy path testing involves testing the primary use case or the most common scenario of an application or a function. It ensures that the code works as expected under normal circumstances.
def calculate_area(length, width):
"""
Calculates the area of a rectangle.
"""
return length * width
# Test case for happy path
def test_calculate_area_happy_path():
length = 5
width = 3
expected_area = 15
assert calculate_area(length, width) == expected_areaSlide 2: Edge Case Testing
Edge cases are inputs or scenarios that are valid but outside the expected or common use cases. They test the boundaries of the system or function.
def validate_age(age):
"""
Validates if the age is between 0 and 120.
"""
if age < 0 or age > 120:
raise ValueError("Age must be between 0 and 120")
return True
# Test cases for edge cases
def test_validate_age_edge_cases():
assert validate_age(0) is True
assert validate_age(120) is True
assert validate_age(-1) is False
assert validate_age(121) is FalseSlide 3: Negative Test Cases
Negative test cases are scenarios where the input is invalid or unexpected. They help catch bugs and ensure the code handles errors and exceptions correctly.
def divide_numbers(a, b):
"""
Divides two numbers and returns the result.
"""
if b == 0:
raise ZeroDivisionError("Cannot divide by zero")
return a / b
# Test case for negative scenario
def test_divide_numbers_negative():
with pytest.raises(ZeroDivisionError):
divide_numbers(10, 0)Slide 4: Security and Illegal Input Testing
Security testing ensures that the application or function is secure against potential threats, such as SQL injection, cross-site scripting (XSS), and other vulnerabilities. Illegal input testing checks how the code handles invalid or malicious inputs.
import re
def sanitize_input(user_input):
"""
Sanitizes user input to prevent XSS attacks.
"""
return re.sub(r'<script>.*?</script>', '', user_input, flags=re.DOTALL)
# Test case for XSS attack
def test_sanitize_input_xss():
malicious_input = "<script>alert('XSS Attack')</script>"
sanitized_input = sanitize_input(malicious_input)
assert sanitized_input == ""Slide 5: Sankey Diagrams
Sankey diagrams are a specific type of flow diagram that visualizes the flow of data or quantities through a system or process. They can be useful for understanding and testing complex systems or data pipelines.
import matplotlib.pyplot as plt
import pandas as pd
data = pd.DataFrame({
'Source': ['A', 'A', 'B', 'C'],
'Target': ['B', 'C', 'D', 'D'],
'Value': [5, 3, 1, 2]
})
# Create a Sankey diagram
fig = plt.figure()
sankey = Sankey(flows=data, labels=['A', 'B', 'C', 'D'], orientations=[0, 1, 0, -1])
sankey.add(flows=data['Value'], alpha=0.5)
sankey.finish()
plt.show()Slide 6: Test Driven Development (TDD)
Test-Driven Development (TDD) is a software development approach where tests are written before the actual code. It helps ensure the code is testable and encourages modular, maintainable design.
# Test case for a function to calculate the factorial of a number
def test_factorial():
assert factorial(0) == 1
assert factorial(1) == 1
assert factorial(5) == 120
assert factorial(-1) is None # Negative numbers should return None
# Implementation of the factorial function
def factorial(n):
if n < 0:
return None
elif n == 0 or n == 1:
return 1
else:
return n * factorial(n - 1)Slide 7: Mocking and Stubbing
Mocking and stubbing are techniques used in unit testing to isolate the code under test from its dependencies. Mocks simulate the behavior of external resources or components, while stubs provide predetermined responses to function calls.
import unittest
from unittest.mock import patch, Mock
class TestMyClass:
@patch('module.external_function')
def test_my_function(self, mock_external_function):
mock_external_function.return_value = 42
result = my_class.my_function()
assert result == 42
mock_external_function.assert_called_once()
@patch('module.external_resource')
def test_my_other_function(self, mock_external_resource):
mock_resource = Mock()
mock_external_resource.return_value = mock_resource
my_class.my_other_function()
mock_resource.some_method.assert_called_once()Slide 8: Parameterized Testing
Parameterized testing is a technique where a single test case is executed multiple times with different input data sets. This helps reduce code duplication and ensures thorough testing of various scenarios.
import pytest
@pytest.mark.parametrize("input_data, expected_output", [
([1, 2, 3], 6),
([-2, 4, -1], 1),
([], 0),
([1.5, 2.7, 3.8], 8.0)
])
def test_sum_list(input_data, expected_output):
assert sum(input_data) == expected_outputSlide 9: Code Coverage
Code coverage is a measure of how much of the codebase is executed by the test suite. It helps identify untested or missing code paths and ensures adequate testing coverage.
# Example function to calculate the factorial of a number
def factorial(n):
if n < 0:
return None
elif n == 0 or n == 1:
return 1
else:
return n * factorial(n - 1)
# Test case for the factorial function
def test_factorial():
assert factorial(0) == 1
assert factorial(1) == 1
assert factorial(5) == 120
assert factorial(-1) is NoneSlide 10: Integration Testing
Integration testing is a type of testing that verifies the proper interaction and communication between different components or modules of a system.
import requests
def test_api_integration():
# Set up test data
payload = {'name': 'John Doe', 'email': 'john@example.com'}
# Send a POST request to create a new user
response = requests.post('https://api.example.com/users', json=payload)
assert response.status_code == 201
# Get the user ID from the response
user_id = response.json()['id']
# Send a GET request to retrieve the user details
response = requests.get(f'https://api.example.com/users/{user_id}')
assert response.status_code == 200
assert response.json()['name'] == 'John Doe'
assert response.json()['email'] == 'john@example.com'
# Send a DELETE request to remove the user
response = requests.delete(f'https://api.example.com/users/{user_id}')
assert response.status_code == 204Slide 11: End-to-End Testing
End-to-End (E2E) testing is a type of testing that simulates real-world scenarios and tests the entire application flow from start to finish, including external dependencies and integrations.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def test_login_flow():
# Set up the webdriver
driver = webdriver.Chrome()
driver.get('https://example.com/login')
# Enter login credentials
username_field = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, 'username'))
)
username_field.send_keys('testuser')
password_field = driver.find_element_by_id('password')
password_field.send_keys('testpassword')
# Submit the login form
submit_button = driver.find_element_by_id('submit')
submit_button.click()
# Verify successful login
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, 'welcome-message'))
)
# Cleanup
driver.quit()Slide 12: Performance Testing
Performance testing is used to evaluate the speed, responsiveness, and stability of an application under different workloads and conditions.
import locust
class MyTaskSet(TaskSet):
@task
def my_task(self):
response = self.client.get('/api/data')
print(f'Response time: {response.elapsed.total_seconds() * 1000} ms')
class MyLoadTest(HttpLocust):
task_set = MyTaskSet
min_wait = 1000
max_wait = 5000Slide 13: Additional Resources
For further learning and exploration of Python clean testing practices, here are some recommended resources:
- "Python Testing with pytest" by Brian Okken (Book)
- "Test-Driven Development with Python" by Harry J.W. Percival (Book)
- "Clean Code" by Robert C. Martin (Book)
- "Python Testing" by Madhav Sangewar (Arxiv: https://arxiv.org/abs/2301.07285)
These resources cover various aspects of testing, including test-driven development, best practices, and advanced techniques for writing clean and maintainable tests in Python.